Skip to content

Commit f519842

Browse files
committed
Pytest rates
1 parent 82b727d commit f519842

File tree

10 files changed

+250
-44
lines changed

10 files changed

+250
-44
lines changed

example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# Initialize the trading strategy
1111
trade = Trade(
1212
expert_name="Moving Average Crossover",
13-
version=1.0,
13+
version="1.0",
1414
symbol="EURUSD",
1515
magic_number=567,
1616
lot=0.1,

mqpy/book.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ def __init__(self, symbol: str) -> None:
3939
else:
4040
logger.error(f"Error adding {self.symbol} to the market book. Error: {Mt5.last_error()}")
4141

42-
def get(self) -> dict[str, Any] | None:
42+
def get(self) -> list[Any] | None:
4343
"""Get the market book for the financial instrument.
4444
4545
Returns:
46-
dict[str, Any] | None: The market book data if successful, None otherwise.
46+
list[Any] | None: The market book data if successful, None otherwise.
4747
"""
4848
return Mt5.market_book_get(self.symbol)
4949

mqpy/rates.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from __future__ import annotations
77

8+
from typing import Any
9+
810
import MetaTrader5 as Mt5
911

1012

@@ -40,11 +42,11 @@ def _raise_value_error(msg: str) -> None:
4042
self._tick_volume = [rate[5] for rate in rates]
4143
self._spread = [rate[6] for rate in rates]
4244
self._real_volume = [rate[7] for rate in rates]
43-
except Mt5.Error as e:
45+
except Exception as e:
4446
raise ValueError(f"Failed to create Rates object for symbol {symbol}") from e
4547

4648
@property
47-
def time(self) -> list[int | float]:
49+
def time(self) -> list[Any]:
4850
"""List of timestamps."""
4951
return self._time
5052

@@ -69,16 +71,16 @@ def close(self) -> list[float]:
6971
return self._close
7072

7173
@property
72-
def tick_volume(self) -> list[int]:
74+
def tick_volume(self) -> list[int | float]:
7375
"""List of tick volumes."""
7476
return self._tick_volume
7577

7678
@property
77-
def spread(self) -> list[int]:
79+
def spread(self) -> list[int | float]:
7880
"""List of spreads."""
7981
return self._spread
8082

8183
@property
82-
def real_volume(self) -> list[int]:
84+
def real_volume(self) -> list[int | float]:
8385
"""List of real volumes."""
8486
return self._real_volume

mqpy/trade.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def select_symbol(self) -> None:
113113
Returns:
114114
None
115115
"""
116-
Mt5.symbol_select(self.symbol, True)
116+
Mt5.symbol_select(self.symbol, select=True)
117117

118118
def prepare_symbol(self) -> None:
119119
"""Prepare the trading symbol for opening positions.
@@ -130,7 +130,7 @@ def prepare_symbol(self) -> None:
130130

131131
if not symbol_info.visible:
132132
logger.warning(f"The {self.symbol} is not visible, needed to be switched on.")
133-
if not Mt5.symbol_select(self.symbol, True):
133+
if not Mt5.symbol_select(self.symbol, select=True):
134134
logger.error(
135135
f"The expert advisor {self.expert_name} failed in select the symbol {self.symbol}, turning off."
136136
)

pyproject.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ disable_error_code = [
6767
]
6868
show_error_codes = true
6969
files = "**/*.py"
70+
exclude = ["venv", "mt5", "site-packages", ".*"]
71+
72+
[[tool.mypy.overrides]]
73+
module = "MetaTrader5.*"
74+
ignore_missing_imports = true
75+
76+
[[tool.mypy.overrides]]
77+
module = "_virtualenv"
78+
ignore_errors = true
7079

7180
[tool.ruff]
7281
line-length = 120

setup.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
# """Setup script for the mqpy package.
1+
"""Setup script for the mqpy package.
22
3-
# This module contains the setup configuration for the mqpy package, which provides a Python interface
4-
# for creating Expert Advisors in MetaTrader 5.
5-
# """
3+
This module contains the setup configuration for the mqpy package, which provides a Python interface
4+
for creating Expert Advisors in MetaTrader 5.
5+
"""
6+
7+
from pathlib import Path
68

79
from setuptools import find_packages, setup
810

11+
# Read the README for the long description
12+
with Path("README.md").open() as f:
13+
long_description = f.read()
14+
915
setup(
1016
name="mqpy",
1117
version="0.6.9",
@@ -14,7 +20,7 @@
1420
author="Joao Euko",
1521
author_email="",
1622
description="A library to simplify creating Expert Advisors in MQL5",
17-
long_description=open("README.md").read(),
23+
long_description=long_description,
1824
long_description_content_type="text/markdown",
1925
license="MIT",
2026
entry_points={

tests/conftest.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1+
"""Test fixtures and configuration shared across test modules."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
from typing import Generator
7+
18
import pytest
29

310

411
@pytest.fixture
5-
def test_symbols():
12+
def test_symbols() -> dict[str, str]:
613
"""Provides common test symbols that can be used across tests."""
714
return {"forex": "EURUSD", "indices": "US500", "commodities": "XAUUSD", "crypto": "BTCUSD", "invalid": "INVALID"}
815

916

1017
@pytest.fixture
11-
def configure_logging():
18+
def configure_logging() -> Generator[None, None, None]:
1219
"""Sets up logging configuration for tests."""
13-
import logging
14-
1520
root = logging.getLogger()
1621
for handler in root.handlers[:]:
1722
root.removeHandler(handler)

tests/test_book.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1+
"""Tests for the Book class that manages market depth information from MetaTrader 5."""
2+
3+
from __future__ import annotations
4+
15
import logging
26
import time
7+
from typing import TYPE_CHECKING, Generator
38

49
import MetaTrader5 as Mt5
510
import pytest
611

12+
if TYPE_CHECKING:
13+
from _pytest.logging import LogCaptureFixture
14+
715
from mqpy.book import Book
816

917

1018
@pytest.fixture(scope="module", autouse=True)
11-
def setup_teardown():
19+
def setup_teardown() -> Generator[None, None, None]:
1220
"""Set up and tear down MetaTrader5 connection for the test module."""
1321
if not Mt5.initialize():
1422
pytest.skip("MetaTrader5 could not be initialized")
@@ -21,7 +29,7 @@ def setup_teardown():
2129

2230

2331
@pytest.fixture
24-
def symbol():
32+
def symbol() -> str:
2533
"""Provides a valid trading symbol for testing."""
2634
time.sleep(1)
2735

@@ -36,15 +44,16 @@ def symbol():
3644
return symbols[0].name
3745

3846

39-
def test_book_initialization(symbol, caplog):
47+
def test_book_initialization(symbol: str, caplog: LogCaptureFixture) -> None:
4048
"""Test initialization of Book with a real symbol."""
4149
caplog.set_level(logging.INFO)
42-
book = Book(symbol)
50+
# Create book instance (used to trigger log message)
51+
Book(symbol)
4352

4453
assert f"The symbol {symbol} was successfully added to the market book" in caplog.text
4554

4655

47-
def test_book_get(symbol):
56+
def test_book_get(symbol: str) -> None:
4857
"""Test getting real market book data."""
4958
book = Book(symbol)
5059

@@ -57,16 +66,23 @@ def test_book_get(symbol):
5766
if market_data:
5867
assert isinstance(market_data, list)
5968

60-
has_bids = any(item.type == Mt5.BOOK_TYPE_SELL for item in market_data)
61-
has_asks = any(item.type == Mt5.BOOK_TYPE_BUY for item in market_data)
69+
# Loop separately to check for bids and asks
70+
has_bids = False
71+
has_asks = False
72+
73+
for item in market_data:
74+
if item.type == Mt5.BOOK_TYPE_SELL:
75+
has_bids = True
76+
if item.type == Mt5.BOOK_TYPE_BUY:
77+
has_asks = True
6278

6379
if not (has_bids or has_asks):
64-
print(f"Warning: No bids or asks found in market book for {symbol}")
80+
logging.warning(f"No bids or asks found in market book for {symbol}")
6581

6682
book.release()
6783

6884

69-
def test_book_release(symbol):
85+
def test_book_release(symbol: str) -> None:
7086
"""Test releasing the market book."""
7187
book = Book(symbol)
7288

@@ -75,7 +91,7 @@ def test_book_release(symbol):
7591
assert result is True
7692

7793

78-
def test_full_workflow(symbol):
94+
def test_full_workflow(symbol: str) -> None:
7995
"""Test a complete workflow with the real market book."""
8096
book = Book(symbol)
8197

@@ -92,10 +108,10 @@ def test_full_workflow(symbol):
92108
data_after_release = book.get()
93109

94110
if data_after_release is not None and len(data_after_release) > 0:
95-
print("Note: Market book data still available after release")
111+
logging.info("Market book data still available after release")
96112

97113

98-
def test_multiple_symbols():
114+
def test_multiple_symbols() -> None:
99115
"""Test using Book with multiple symbols simultaneously."""
100116
symbols = Mt5.symbols_get()
101117
if len(symbols) < 2:
@@ -119,7 +135,7 @@ def test_multiple_symbols():
119135
book2.release()
120136

121137

122-
def test_unavailable_symbol(caplog):
138+
def test_unavailable_symbol(caplog: LogCaptureFixture) -> None:
123139
"""Test behavior with an unavailable symbol."""
124140
caplog.set_level(logging.ERROR)
125141

0 commit comments

Comments
 (0)