Files
Ichinga Samuel eed7e80c0d v4.1.0
2026-02-27 04:11:38 +01:00

324 lines
11 KiB
Python

"""Comprehensive tests for the Symbol class.
This module contains live tests for the Symbol class, which provides
the interface for interacting with trading instruments in MetaTrader 5.
"""
from datetime import datetime, timedelta
import pytest
from aiomql.lib.symbol import Symbol
from aiomql.lib.candle import Candles
from aiomql.lib.ticks import Tick, Ticks
from aiomql.core.models import SymbolInfo, BookInfo
class TestSymbolInit:
"""Tests for Symbol initialisation."""
async def test_requires_name(self):
"""Test that Symbol raises AssertionError without a name."""
with pytest.raises(AssertionError):
Symbol()
async def test_initialized_starts_false(self):
"""Test that a newly created Symbol is not initialized."""
sym = Symbol(name="BTCUSD")
assert sym.initialized is False
async def test_has_account(self):
"""Test that a newly created Symbol has an account attribute."""
sym = Symbol(name="BTCUSD")
assert sym.account is not None
async def test_name_attribute(self):
"""Test that the name attribute is set correctly."""
sym = Symbol(name="BTCUSD")
assert sym.name == "BTCUSD"
class TestSymbolInitialize:
"""Tests for Symbol.initialize() and Symbol.initialize_sync()."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_initialize_returns_true(self, btc):
"""Test that initialize() returns True for a valid symbol."""
assert btc.initialized is True
async def test_initialize_sets_select(self, btc):
"""Test that initialize() sets the select attribute."""
assert btc.select is True
async def test_initialize_sets_tick(self, btc):
"""Test that initialize() sets the tick attribute."""
assert btc.tick is not None
assert isinstance(btc.tick, Tick)
async def test_initialize_sets_symbol_properties(self, btc):
"""Test that initialize() populates SymbolInfo properties."""
assert btc.volume_min > 0
assert btc.volume_max > 0
assert btc.volume_step > 0
assert btc.point > 0
async def test_initialize_sync(self):
"""Test that initialize_sync() works correctly."""
sym = Symbol(name="BTCUSD")
result = sym.initialize_sync()
assert result is True
assert sym.initialized is True
assert sym.tick is not None
class TestSymbolInfo:
"""Tests for Symbol.info() and Symbol.info_tick()."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_info_returns_symbol_info(self, btc):
"""Test that info() returns a SymbolInfo object."""
result = await btc.info()
assert result is not None
assert isinstance(result, SymbolInfo)
async def test_info_tick_returns_tick(self, btc):
"""Test that info_tick() returns a Tick object."""
tick = await btc.info_tick()
assert tick is not None
assert isinstance(tick, Tick)
async def test_info_tick_updates_tick_attribute(self, btc):
"""Test that info_tick() updates the symbol's tick attribute."""
tick = await btc.info_tick()
assert btc.tick is tick
async def test_info_tick_with_name(self, btc):
"""Test info_tick() with an explicit symbol name."""
tick = await btc.info_tick(name="ETHUSD")
assert tick is not None
assert isinstance(tick, Tick)
class TestSymbolSelect:
"""Tests for Symbol.symbol_select()."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_symbol_select_enable(self, btc):
"""Test that symbol_select(enable=True) selects the symbol."""
result = await btc.symbol_select(enable=True)
assert result is True
assert btc.select is True
async def test_symbol_select_disable_and_renable(self, btc):
"""Test toggling symbol selection off and on."""
await btc.symbol_select(enable=False)
assert btc.select is False
await btc.symbol_select(enable=True)
assert btc.select is True
class TestSymbolBook:
"""Tests for Symbol market depth (book) methods."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_book_add(self, btc):
"""Test that book_add() returns a boolean."""
result = await btc.book_add()
assert isinstance(result, bool)
async def test_book_get(self, btc):
"""Test that book_get() returns a tuple of BookInfo."""
await btc.book_add()
try:
books = await btc.book_get()
assert isinstance(books, tuple)
if len(books) > 0:
assert isinstance(books[0], BookInfo)
except ValueError:
# Market depth may not be available for all symbols
pass
async def test_book_release(self, btc):
"""Test that book_release() returns a boolean."""
result = await btc.book_release()
assert isinstance(result, bool)
class TestSymbolVolume:
"""Tests for Symbol volume-related methods."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_check_volume_below_min(self, btc):
"""Test check_volume with volume below minimum."""
volume = btc.volume_min - btc.volume_step
success, result_vol = btc.check_volume(volume=volume)
assert success is False
assert result_vol == btc.volume_min
async def test_check_volume_valid(self, btc):
"""Test check_volume with a valid volume."""
volume = btc.volume_min + btc.volume_step * 2
success, result_vol = btc.check_volume(volume=volume)
assert success is True
assert result_vol == volume
async def test_check_volume_above_max(self, btc):
"""Test check_volume with volume above maximum."""
volume = btc.volume_max + btc.volume_step
success, result_vol = btc.check_volume(volume=volume)
assert success is False
assert result_vol == btc.volume_max
async def test_round_off_volume(self, btc):
"""Test round_off_volume rounds to nearest step."""
volume = btc.volume_min + btc.volume_step * 2.5
rounded = btc.round_off_volume(volume=volume, round_down=True)
assert rounded == btc.volume_min + btc.volume_step * 2
async def test_round_off_volume_up(self, btc):
"""Test round_off_volume rounding up."""
volume = btc.volume_min + btc.volume_step * 2.5
rounded = btc.round_off_volume(volume=volume, round_down=False)
assert rounded == btc.volume_min + btc.volume_step * 3
async def test_compute_volume_returns_min(self, btc):
"""Test that the base compute_volume() returns volume_min."""
result = await btc.compute_volume()
assert result == btc.volume_min
class TestSymbolCurrency:
"""Tests for Symbol currency conversion methods."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_amount_in_quote_currency(self, btc):
"""Test amount_in_quote_currency returns a float."""
result = await btc.amount_in_quote_currency(amount=100.0)
assert isinstance(result, (int, float))
async def test_convert_currency(self, btc):
"""Test convert_currency between two currencies."""
result = await btc.convert_currency(
amount=100.0, from_currency="USD", to_currency="EUR"
)
# Result may be None if the conversion pair is not available
if result is not None:
assert isinstance(result, float)
assert result > 0
class TestSymbolRates:
"""Tests for Symbol copy rates and ticks methods."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_copy_rates_from(self, btc):
"""Test copy_rates_from returns Candles."""
start = datetime(year=2023, month=10, day=5)
rates = await btc.copy_rates_from(
timeframe=btc.mt5.TIMEFRAME_H1, date_from=start, count=10
)
assert isinstance(rates, Candles)
assert len(rates) == 10
async def test_copy_rates_from_pos(self, btc):
"""Test copy_rates_from_pos returns Candles."""
rates = await btc.copy_rates_from_pos(
timeframe=btc.mt5.TIMEFRAME_H1, count=10, start_position=0
)
assert isinstance(rates, Candles)
assert len(rates) == 10
async def test_copy_rates_range(self, btc):
"""Test copy_rates_range returns Candles."""
start = datetime(year=2023, month=10, day=5)
end = start + timedelta(hours=9)
rates = await btc.copy_rates_range(
timeframe=btc.mt5.TIMEFRAME_H1, date_from=start, date_to=end
)
assert isinstance(rates, Candles)
assert len(rates) == 10
async def test_copy_ticks_from(self, btc):
"""Test copy_ticks_from returns Ticks."""
start = datetime(year=2023, month=10, day=5)
ticks = await btc.copy_ticks_from(date_from=start, count=10)
assert isinstance(ticks, Ticks)
assert len(ticks) == 10
async def test_copy_ticks_range(self, btc):
"""Test copy_ticks_range returns Ticks."""
start = datetime(year=2023, month=10, day=5)
end = start + timedelta(seconds=20)
ticks = await btc.copy_ticks_range(date_from=start, date_to=end)
assert isinstance(ticks, Ticks)
assert len(ticks) >= 10
class TestSymbolProperties:
"""Tests for Symbol properties."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_pip_property(self, btc):
"""Test that pip equals point * 10."""
assert btc.pip == btc.point * 10
class TestSymbolAbstract:
"""Tests for Symbol abstract/overridable methods."""
@pytest.fixture(scope="class")
async def btc(self):
symbol = Symbol(name="BTCUSD")
await symbol.initialize()
return symbol
async def test_compute_volume_sl_raises(self, btc):
"""Test that compute_volume_sl raises NotImplementedError."""
with pytest.raises(NotImplementedError):
await btc.compute_volume_sl(amount=100.0, price=50000.0, sl=49500.0)
async def test_compute_volume_points_raises(self, btc):
"""Test that compute_volume_points raises NotImplementedError."""
with pytest.raises(NotImplementedError):
await btc.compute_volume_points(amount=100.0, points=500.0)