This commit is contained in:
Ichinga Samuel
2024-11-04 00:32:51 +01:00
parent 2c46e18528
commit d65a8db1a2
85 changed files with 5071 additions and 2512 deletions
View File
+45 -27
View File
@@ -8,7 +8,7 @@ from pathlib import Path
import pytest
from aiomql.core import Config
from aiomql.core.meta_backtester import MetaBackTester
from aiomql.contrib import BackTestEngine
from aiomql.core.backtesting.backtest_engine import BackTestEngine
from aiomql.lib import Positions, History, Order
logger = getLogger(__name__)
@@ -16,10 +16,10 @@ logger = getLogger(__name__)
async def cleanup():
try:
shutil.rmtree(Path('tests/backtest/configs'), ignore_errors=True)
Path.unlink(Path('tests/backtest/test.json'), missing_ok=True)
shutil.rmtree(Path('tests/backtest/trade_records'), ignore_errors=True)
shutil.rmtree(Path('tests/backtest/backtesting'), ignore_errors=True)
shutil.rmtree(Path("tests/backtest/configs"), ignore_errors=True)
Path.unlink(Path("tests/backtest/test.json"), missing_ok=True)
shutil.rmtree(Path("tests/backtest/trade_records"), ignore_errors=True)
shutil.rmtree(Path("tests/backtest/backtesting"), ignore_errors=True)
await close_all_positions()
await MetaBackTester().shutdown()
except Exception as err:
@@ -32,29 +32,41 @@ async def close_all_positions():
positions = await mt.positions_get()
tasks = []
for position in positions:
order_type = mt.ORDER_TYPE_BUY if position.type == mt.ORDER_TYPE_SELL else mt.ORDER_TYPE_SELL
req = {'action': mt.TRADE_ACTION_DEAL, 'symbol': position.symbol, 'volume': position.volume,
'type': order_type, 'position': position.ticket, 'price': position.price_current}
order_type = (
mt.ORDER_TYPE_BUY
if position.type == mt.ORDER_TYPE_SELL
else mt.ORDER_TYPE_SELL
)
req = {
"action": mt.TRADE_ACTION_DEAL,
"symbol": position.symbol,
"volume": position.volume,
"type": order_type,
"position": position.ticket,
"price": position.price_current,
}
tasks.append(mt.order_send(req))
await asyncio.gather(*tasks)
except Exception as err:
logger.error(f"Failed to close all positions: {err}")
@pytest.fixture(scope='package', autouse=True)
@pytest.fixture(scope="package", autouse=True)
async def config(request):
Path('tests/backtest/configs').mkdir(exist_ok=True)
with open('aiomql.json', 'r') as fh, open('tests/backtest/configs/test2.json', 'w') as fh1, open('tests/backtest/test.json', 'w') as fh2:
Path("tests/backtest/configs").mkdir(exist_ok=True)
with open("aiomql.json", "r") as fh, open(
"tests/backtest/configs/test2.json", "w"
) as fh1, open("tests/backtest/test.json", "w") as fh2:
data = json.load(fh)
data['mode'] = 'backtest'
data["mode"] = "backtest"
json.dump(data, fh1, indent=2)
json.dump(data, fh2, indent=2)
config = Config(filename='test.json', root='tests/backtest')
config = Config(filename="test.json", root="tests/backtest")
yield config
await cleanup()
@pytest.fixture(scope='package', autouse=True)
@pytest.fixture(scope="package", autouse=True)
async def mt():
mt = MetaBackTester()
await mt.initialize()
@@ -62,35 +74,41 @@ async def mt():
yield mt
await mt.shutdown()
@pytest.fixture(scope='package')
@pytest.fixture(scope="package")
async def period():
return {'start': datetime(2024, 2, 1, hour=8, tzinfo=UTC),
'end': datetime(2024, 2, 7, hour=16, tzinfo=UTC)}
return {
"start": datetime(2024, 2, 1, hour=8, tzinfo=UTC),
"end": datetime(2024, 2, 7, hour=16, tzinfo=UTC),
}
@pytest.fixture(scope='package')
@pytest.fixture(scope="package")
async def backtest_engine(period):
start = period['start']
end = period['end']
return BackTestEngine(start=start, end=end, name='backtest_data')
start = period["start"]
end = period["end"]
return BackTestEngine(
start=start, end=end, name="backtest_data", assign_to_config=True, preload=False
)
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
def order_sell(sell_order):
return Order(**sell_order)
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
def order_buy(buy_order):
return Order(**buy_order)
@pytest.fixture(scope='package')
@pytest.fixture(scope="package")
def positions():
return Positions()
@pytest.fixture(scope='package')
@pytest.fixture(scope="package")
def history(period):
start = period['start']
end = period['end']
start = period["start"]
end = period["end"]
return History(date_from=start, date_to=end)
+50 -23
View File
@@ -1,27 +1,36 @@
from aiomql.contrib import BackTestEngine, ForexSymbol, GetData
from aiomql.core import MetaBackTester
from aiomql.contrib import ForexSymbol
from aiomql.core import MetaBackTester, BackTestEngine, GetData
from aiomql.lib import Order
async def make_buy_sell_orders():
sym = ForexSymbol(name='BTCUSD')
sym = ForexSymbol(name="BTCUSD")
sym_info = await sym.mt5.symbol_info(sym.name)
dsl = (sym_info.trade_stops_level + sym_info.spread) * 2 * sym_info.point
sl = sym_info.ask - dsl
tp = sym_info.ask + dsl
buy_req = {'action': sym.mt5.TRADE_ACTION_DEAL, 'symbol': sym.name, 'volume': sym_info.volume_min,
'type': sym.mt5.ORDER_TYPE_BUY, 'price': sym_info.ask, 'sl': sl, 'tp': tp}
buy_req = {
"action": sym.mt5.TRADE_ACTION_DEAL,
"symbol": sym.name,
"volume": sym_info.volume_min,
"type": sym.mt5.ORDER_TYPE_BUY,
"price": sym_info.ask,
"sl": sl,
"tp": tp,
}
sell_req = buy_req.copy()
sell_req['type'] = sym.mt5.ORDER_TYPE_SELL
sell_req['price'] = sym_info.bid
del sell_req['tp']
del sell_req['sl']
return {'buy': Order(**buy_req), 'sell': Order(**sell_req)}
sell_req["type"] = sym.mt5.ORDER_TYPE_SELL
sell_req["price"] = sym_info.bid
del sell_req["tp"]
del sell_req["sl"]
return {"buy": Order(**buy_req), "sell": Order(**sell_req)}
def test_trade_mode(config, backtest_engine, history, positions, order_sell, order_buy, btc_usd):
assert config.mode == 'backtest'
def test_trade_mode(
config, backtest_engine, history, positions, order_sell, order_buy, btc_usd
):
assert config.mode == "backtest"
assert isinstance(backtest_engine, BackTestEngine)
assert isinstance(history.mt5, MetaBackTester)
assert isinstance(positions.mt5, MetaBackTester)
@@ -69,15 +78,27 @@ async def test_history(backtest_engine, history, order_sell, order_buy, position
async def test_margin(backtest_engine, order_sell, order_buy):
await backtest_engine.setup_account(balance=100)
so_margin = await backtest_engine.order_calc_margin(action=order_sell.action, volume=order_sell.volume,
symbol=order_sell.symbol, price=order_sell.price)
bo_margin = await backtest_engine.order_calc_margin(action=order_buy.action, volume=order_buy.volume,
symbol=order_buy.symbol, price=order_buy.price)
so_margin = await backtest_engine.order_calc_margin(
action=order_sell.action,
volume=order_sell.volume,
symbol=order_sell.symbol,
price=order_sell.price,
)
bo_margin = await backtest_engine.order_calc_margin(
action=order_buy.action,
volume=order_buy.volume,
symbol=order_buy.symbol,
price=order_buy.price,
)
total_margin = so_margin + bo_margin
await backtest_engine.order_send(request=order_sell.request)
await backtest_engine.order_send(request=order_buy.request)
# noinspection PyTestUnpassedFixture
assert backtest_engine.positions.margin == total_margin == backtest_engine._account.margin
assert (
backtest_engine.positions.margin
== total_margin
== backtest_engine._account.margin
)
backtest_engine.reset(clear_data=True)
@@ -87,8 +108,8 @@ async def test_account(backtest_engine, positions):
balance = backtest_engine._account.balance
equity = backtest_engine._account.equity
orders = await make_buy_sell_orders()
buy_order = orders['buy']
sell_order = orders['sell']
buy_order = orders["buy"]
sell_order = orders["sell"]
so = await backtest_engine.order_send(request=sell_order.request)
bo = await backtest_engine.order_send(request=buy_order.request)
backtest_engine.fast_forward(steps=22000)
@@ -103,7 +124,11 @@ async def test_account(backtest_engine, positions):
deal = backtest_engine.deals.history_deals_get(position=bo.order)
bo_profit = deal[-1].profit
assert len(all_pos) == 1
assert backtest_engine.positions.margin == backtest_engine._account.margin == backtest_engine.positions.margins[so.order]
assert (
backtest_engine.positions.margin
== backtest_engine._account.margin
== backtest_engine.positions.margins[so.order]
)
profit = sum([pos.profit for pos in all_pos])
n_balance = backtest_engine._account.balance
n_equity = backtest_engine._account.equity
@@ -128,9 +153,11 @@ async def test_wrapup(positions, buy_order, sell_order, backtest_engine, config)
last_balance = backtest_engine._account.balance
last_equity = backtest_engine._account.equity
last_profit = backtest_engine._account.profit
backtest_engine.wrap_up()
tdata = GetData.load_data(name=config.backtest_dir / f'{backtest_engine.name}.pkl')
new_bte = BackTestEngine(data=tdata, restart=False)
await backtest_engine.wrap_up()
tdata = GetData.load_data(name=config.backtest_dir / f"{backtest_engine.name}.pkl")
new_bte = BackTestEngine(
data=tdata, restart=False, assign_to_config=False, preload=False
)
assert new_bte._account.balance == last_balance
assert new_bte._account.equity == last_equity
assert new_bte._account.profit == last_profit
+7 -3
View File
@@ -5,8 +5,8 @@ async def test_deals_manager(backtest_engine, sell_order, buy_order, period, pos
backtest_engine.fast_forward(steps=100)
await backtest_engine.order_send(request=sell_order)
bo = await backtest_engine.order_send(request=buy_order)
start = period['start']
end = period['end']
start = period["start"]
end = period["end"]
all_deals = backtest_engine.deals.get_deals_range(date_from=start, date_to=end)
assert len(all_deals) == 2
backtest_engine.fast_forward(steps=10_000)
@@ -21,4 +21,8 @@ async def test_deals_manager(backtest_engine, sell_order, buy_order, period, pos
deals = backtest_engine.deals.history_deals_get(position=bo.order)
assert len(deals) <= 2
orders = backtest_engine.deals.get_deals_range(date_from=start, date_to=end)
assert len(orders) == backtest_engine.deals.history_deals_total(date_from=start, date_to=end) == len(backtest_engine.deals._data.keys())
assert (
len(orders)
== backtest_engine.deals.history_deals_total(date_from=start, date_to=end)
== len(backtest_engine.deals._data.keys())
)
+10 -4
View File
@@ -1,12 +1,14 @@
# noinspection PyTestUnpassedFixture
async def test_orders_manager(backtest_engine, sell_order, buy_order, period, positions):
async def test_orders_manager(
backtest_engine, sell_order, buy_order, period, positions
):
backtest_engine.reset(clear_data=True)
await backtest_engine.setup_account(balance=100)
backtest_engine.fast_forward(steps=100)
await backtest_engine.order_send(request=sell_order)
bo = await backtest_engine.order_send(request=buy_order)
start = period['start']
end = period['end']
start = period["start"]
end = period["end"]
all_orders = backtest_engine.orders.get_orders_range(date_from=start, date_to=end)
assert len(all_orders) == 2
backtest_engine.fast_forward(steps=10_000)
@@ -21,4 +23,8 @@ async def test_orders_manager(backtest_engine, sell_order, buy_order, period, po
orders = backtest_engine.orders.history_orders_get(position=bo.order)
assert len(orders) <= 2
orders = backtest_engine.orders.get_orders_range(date_from=start, date_to=end)
assert len(orders) == backtest_engine.orders.history_orders_total(date_from=start, date_to=end) == len(backtest_engine.orders._data.keys())
assert (
len(orders)
== backtest_engine.orders.history_orders_total(date_from=start, date_to=end)
== len(backtest_engine.orders._data.keys())
)
@@ -10,7 +10,7 @@ async def test_positions_manager(backtest_engine, sell_order, buy_order):
so_positions = backtest_engine.positions.positions_get(ticket=so.order)
so_position = so_positions[0]
assert so_position.ticket == so.order
btc_positions = backtest_engine.positions.positions_get(symbol='BTCUSD')
btc_positions = backtest_engine.positions.positions_get(symbol="BTCUSD")
assert len(btc_positions) == 2
assert backtest_engine.positions.positions_total() == 2
backtest_engine.positions.close(ticket=bo.order)
+35 -16
View File
@@ -2,42 +2,61 @@ from aiomql.lib.symbol import Symbol
import pytest
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
def btc_usd():
return Symbol(name='BTCUSD')
return Symbol(name="BTCUSD")
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
async def buy_order(btc_usd):
sym = btc_usd
sym_info = await sym.mt5.symbol_info(sym.name)
dsl = (sym_info.trade_stops_level + sym_info.spread) * 2 * sym_info.point
sl = sym_info.ask - dsl
tp = sym_info.ask + dsl
return {'action': sym.mt5.TRADE_ACTION_DEAL, 'symbol': sym.name, 'volume': sym_info.volume_min,
'type': sym.mt5.ORDER_TYPE_BUY, 'price': sym_info.ask, 'sl': sl, 'tp': tp}
return {
"action": sym.mt5.TRADE_ACTION_DEAL,
"symbol": sym.name,
"volume": sym_info.volume_min,
"type": sym.mt5.ORDER_TYPE_BUY,
"price": sym_info.ask,
"sl": sl,
"tp": tp,
}
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
async def sell_order(btc_usd):
sym = btc_usd
sym_info = await sym.mt5.symbol_info(sym.name)
return {'action': sym.mt5.TRADE_ACTION_DEAL, 'symbol': sym.name, 'volume': sym_info.volume_min,
'type': sym.mt5.ORDER_TYPE_SELL, 'price': sym_info.bid}
return {
"action": sym.mt5.TRADE_ACTION_DEAL,
"symbol": sym.name,
"volume": sym_info.volume_min,
"type": sym.mt5.ORDER_TYPE_SELL,
"price": sym_info.bid,
}
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def make_buy_sell_orders():
sym = Symbol(name='BTCUSD')
sym = Symbol(name="BTCUSD")
sym_info = await sym.mt5.symbol_info(sym.name)
dsl = (sym_info.trade_stops_level + sym_info.spread) * 2 * sym_info.point
sl = sym_info.ask - dsl
tp = sym_info.ask + dsl
req = {'action': sym.mt5.TRADE_ACTION_DEAL, 'symbol': sym.name, 'volume': sym_info.volume_min,
'type': sym.mt5.ORDER_TYPE_BUY, 'price': sym_info.ask, 'sl': sl, 'tp': tp}
req = {
"action": sym.mt5.TRADE_ACTION_DEAL,
"symbol": sym.name,
"volume": sym_info.volume_min,
"type": sym.mt5.ORDER_TYPE_BUY,
"price": sym_info.ask,
"sl": sl,
"tp": tp,
}
await sym.mt5.order_send(req)
req['type'] = sym.mt5.ORDER_TYPE_SELL
req['price'] = sym_info.bid
req['sl'] = sym_info.bid + dsl
req['tp'] = sym_info.bid - dsl
req["type"] = sym.mt5.ORDER_TYPE_SELL
req["price"] = sym_info.bid
req["sl"] = sym_info.bid + dsl
req["tp"] = sym_info.bid - dsl
await sym.mt5.order_send(req)
+24 -12
View File
@@ -13,10 +13,10 @@ logger = getLogger(__name__)
async def cleanup():
try:
shutil.rmtree(Path('tests/live/configs'), ignore_errors=True)
Path.unlink(Path('tests/live/test.json'), missing_ok=True)
shutil.rmtree(Path('tests/live/trade_records'), ignore_errors=True)
shutil.rmtree(Path('tests/live/backtesting'), ignore_errors=True)
shutil.rmtree(Path("tests/live/configs"), ignore_errors=True)
Path.unlink(Path("tests/live/test.json"), missing_ok=True)
shutil.rmtree(Path("tests/live/trade_records"), ignore_errors=True)
shutil.rmtree(Path("tests/live/backtesting"), ignore_errors=True)
await close_all_positions()
await MetaTrader().shutdown()
except Exception as err:
@@ -29,28 +29,40 @@ async def close_all_positions():
positions = await mt.positions_get()
tasks = []
for position in positions:
order_type = mt.ORDER_TYPE_BUY if position.type == mt.ORDER_TYPE_SELL else mt.ORDER_TYPE_SELL
req = {'action': mt.TRADE_ACTION_DEAL, 'symbol': position.symbol, 'volume': position.volume,
'type': order_type, 'position': position.ticket, 'price': position.price_current}
order_type = (
mt.ORDER_TYPE_BUY
if position.type == mt.ORDER_TYPE_SELL
else mt.ORDER_TYPE_SELL
)
req = {
"action": mt.TRADE_ACTION_DEAL,
"symbol": position.symbol,
"volume": position.volume,
"type": order_type,
"position": position.ticket,
"price": position.price_current,
}
tasks.append(mt.order_send(req))
await asyncio.gather(*tasks)
except Exception as err:
logger.error(f"Failed to close all positions: {err}")
@pytest.fixture(scope='package', autouse=True)
@pytest.fixture(scope="package", autouse=True)
async def config(request):
Path('tests/live/configs').mkdir(exist_ok=True)
with open('aiomql.json', 'r') as fh, open('tests/live/configs/test2.json', 'w') as fh1, open('tests/live/test.json', 'w') as fh2:
Path("tests/live/configs").mkdir(exist_ok=True)
with open("aiomql.json", "r") as fh, open(
"tests/live/configs/test2.json", "w"
) as fh1, open("tests/live/test.json", "w") as fh2:
data = json.load(fh)
json.dump(data, fh1, indent=2)
json.dump(data, fh2, indent=2)
config = Config(filename='test.json', root='tests/live')
config = Config(root="tests/live", filename="test.json")
yield config
await cleanup()
@pytest.fixture(scope='package', autouse=True)
@pytest.fixture(scope="package", autouse=True)
async def mt():
mt = MetaTrader()
await mt.initialize()
+8 -6
View File
@@ -6,17 +6,19 @@ from aiomql.contrib.symbols import ForexSymbol
async def test_bot():
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
syms = ['BTCUSD', 'SOLUSD', 'ETHUSD']
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
syms = ["BTCUSD", "SOLUSD", "ETHUSD"]
symbols = [ForexSymbol(name=sym) for sym in syms]
stgs = [Chaos(symbol=symbol, name='test_chaos') for symbol in symbols]
strategies = [Chaos(symbol=symbol, name="test_chaos") for symbol in symbols]
bot = Bot()
assert bot.config.shutdown is False
bot.executor.timeout = 30
bot.add_strategies(strategies=stgs)
bot.executor.timeout = 10
bot.add_strategies(strategies=strategies)
await bot.initialize()
await bot.executor.execute()
assert bot.executor.no_of_running_strategies == 3
assert len(bot.executor.coroutines) == 1
assert len(bot.executor.coroutine_threads) == 1
assert bot.config.shutdown is True
+49 -21
View File
@@ -15,35 +15,59 @@ class TestRecordsAndResults:
def setup_class(cls):
cls.trade_records = TradeRecords()
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def buy(self, mt):
sym = 'BTCUSD'
sym = "BTCUSD"
sym_info = await mt.symbol_info(sym)
dsl = (sym_info.trade_stops_level + sym_info.spread) * 2 * sym_info.point
sl = sym_info.ask - dsl
tp = sym_info.ask + dsl
return {'action': mt.TRADE_ACTION_DEAL, 'symbol': sym, 'volume': sym_info.volume_min,
'type': mt.ORDER_TYPE_BUY, 'price': sym_info.ask, 'sl': sl, 'tp': tp}
return {
"action": mt.TRADE_ACTION_DEAL,
"symbol": sym,
"volume": sym_info.volume_min,
"type": mt.ORDER_TYPE_BUY,
"price": sym_info.ask,
"sl": sl,
"tp": tp,
}
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def sell(self, mt):
sym = 'BTCUSD'
sym = "BTCUSD"
sym_info = await mt.symbol_info(sym)
return {'action': mt.TRADE_ACTION_DEAL, 'symbol': sym, 'volume': sym_info.volume_min,
'type': mt.ORDER_TYPE_SELL, 'price': sym_info.bid}
return {
"action": mt.TRADE_ACTION_DEAL,
"symbol": sym,
"volume": sym_info.volume_min,
"type": mt.ORDER_TYPE_SELL,
"price": sym_info.bid,
}
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def setup(self, sell, buy, mt):
buy_res = await mt.order_send(buy)
buy_res_2 = await mt.order_send(buy)
sell_res = await mt.order_send(sell)
sell_res_2 = await mt.order_send(sell)
buy_res = Result(result=OrderSendResult(**buy_res._asdict()), name='test_result')
sell_res = Result(result=OrderSendResult(**sell_res._asdict()), name='test_result')
sell_res_2 = Result(result=OrderSendResult(**sell_res_2._asdict()), name='test_result')
buy_res_2 = Result(result=OrderSendResult(**buy_res_2._asdict()), name='test_result')
await asyncio.gather(buy_res.save(), sell_res.save(), buy_res_2.save(trade_record_mode='json'),
sell_res_2.save(trade_record_mode='json'))
buy_res = Result(
result=OrderSendResult(**buy_res._asdict()), name="test_result"
)
sell_res = Result(
result=OrderSendResult(**sell_res._asdict()), name="test_result"
)
sell_res_2 = Result(
result=OrderSendResult(**sell_res_2._asdict()), name="test_result"
)
buy_res_2 = Result(
result=OrderSendResult(**buy_res_2._asdict()), name="test_result"
)
await asyncio.gather(
buy_res.save(),
sell_res.save(),
buy_res_2.save(trade_record_mode="json"),
sell_res_2.save(trade_record_mode="json"),
)
await Positions().close_all()
def test_records_dir(self):
@@ -66,29 +90,33 @@ class TestRecordsAndResults:
async def test_json_records(self):
json_records = self.trade_records.get_json_records()
matched_recs = [record for record in json_records if record.match('test_result.json')]
matched_recs = [
record for record in json_records if record.match("test_result.json")
]
assert len(matched_recs) == 1
record = matched_recs[0]
record_data = json.load(record.open())
assert isinstance(record_data, list)
assert len(record_data) == 2
is_open = [data['closed'] is False for data in record_data]
is_open = [data["closed"] is False for data in record_data]
assert all(is_open)
await self.trade_records.update_json_records()
is_close = [data['closed'] is True for data in record_data]
is_close = [data["closed"] is True for data in record_data]
assert len(is_close) == 2
async def test_csv_records(self):
csv_records = self.trade_records.get_csv_records()
matched_recs = [record for record in csv_records if record.match('test_result.csv')]
matched_recs = [
record for record in csv_records if record.match("test_result.csv")
]
assert len(matched_recs) == 1
record = matched_recs[0]
record_data = DictReader(record.open())
record_data = [row for row in record_data]
assert isinstance(record_data, list)
assert len(record_data) == 2
is_open = [data['closed'].title() == 'False' for data in record_data]
is_open = [data["closed"].title() == "False" for data in record_data]
assert all(is_open)
await self.trade_records.update_json_records()
is_close = [data['closed'].title() == 'True' for data in record_data]
is_close = [data["closed"].title() == "True" for data in record_data]
assert len(is_close) == 2
+2 -2
View File
@@ -1,13 +1,13 @@
import pytest
from aiomql.lib.account import Account
class TestAccount:
class TestAccount:
@classmethod
def setup_class(cls):
cls.account = Account()
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def refresh(self):
await self.account.refresh()
+123 -51
View File
@@ -1,9 +1,9 @@
from datetime import datetime, UTC
from aiomql import TimeFrame
from aiomql.contrib.backtesting import BackTestEngine
from aiomql.contrib.backtesting.get_data import GetData
from aiomql._utils import round_down, round_up
from aiomql.core.backtesting import BackTestEngine
from aiomql.core.backtesting.get_data import GetData
from aiomql._utils import round_down
from aiomql.core.constants import OrderType, TradeAction
import pytest
@@ -14,49 +14,79 @@ class TestBackTestEngine:
def setup_class(cls):
cls.start = datetime(2024, 2, 1)
cls.end = datetime(2024, 2, 7)
cls.g_data = GetData(start=cls.start, end=cls.end, symbols=['BTCUSD', 'SOLUSD'],
timeframes=[TimeFrame.H1, TimeFrame.H2], name='test_engine')
cls.bte = BackTestEngine(start=cls.start, end=cls.end)
cls.g_data = GetData(
start=cls.start,
end=cls.end,
symbols=["BTCUSD", "SOLUSD"],
timeframes=[TimeFrame.H1, TimeFrame.H2],
name="test_engine",
)
cls.bte = BackTestEngine(
start=cls.start, end=cls.end, assign_to_config=True, preload=False
)
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def bte2(self):
await self.g_data.get_data()
bte2 = BackTestEngine(start=self.start, end=self.end, data=self.g_data.data, use_terminal=False)
bte2 = BackTestEngine(
start=self.start,
end=self.end,
data=self.g_data.data,
use_terminal=False,
preload=False,
)
await bte2.setup_account(balance=100)
return bte2
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def sell_order(self):
sym = await self.bte.get_symbol_info(symbol='BTCUSD')
request = {'type': OrderType.SELL, 'symbol': 'BTCUSD', 'volume': sym.volume_min,
'price': sym.bid, 'action': TradeAction.DEAL}
sym = await self.bte.get_symbol_info(symbol="BTCUSD")
request = {
"type": OrderType.SELL,
"symbol": "BTCUSD",
"volume": sym.volume_min,
"price": sym.bid,
"action": TradeAction.DEAL,
}
return request
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def buy_order(self):
sym = await self.bte.get_symbol_info(symbol='BTCUSD')
sym = await self.bte.get_symbol_info(symbol="BTCUSD")
dsl = (sym.trade_stops_level + sym.spread) * 2 * sym.point
sl = sym.ask - dsl
tp = sym.ask + dsl
request = {'type': OrderType.BUY, 'symbol': 'BTCUSD', 'volume': sym.volume_min,
'price': sym.ask, 'action': TradeAction.DEAL, 'sl': sl, 'tp': tp}
request = {
"type": OrderType.BUY,
"symbol": "BTCUSD",
"volume": sym.volume_min,
"price": sym.ask,
"action": TradeAction.DEAL,
"sl": sl,
"tp": tp,
}
return request
def modify_stops(self, order):
...
def test_span_and_range(self):
assert self.bte.range == range(0, int((self.end - self.start).total_seconds()), self.bte.speed)
assert self.bte.span == range(int(self.start.timestamp()), int(self.end.timestamp()), self.bte.speed)
assert self.bte.range == range(
0, int((self.end - self.start).total_seconds()), self.bte.speed
)
assert self.bte.span == range(
int(self.start.timestamp()), int(self.end.timestamp()), self.bte.speed
)
assert len(self.bte.span) == len(self.bte.range)
def test_cursor(self):
self.bte.next()
r, t = self.bte.cursor
self.bte.fast_forward(steps=100)
assert self.bte.cursor.time == t + 100
assert self.bte.cursor.index == r + 100
go_to = datetime(2024, 2, 3, tzinfo=UTC)
assert self.bte.cursor.time == t + 100 * self.bte.speed
assert self.bte.cursor.index == r + 100 * self.bte.speed
print(datetime.fromtimestamp(self.bte.cursor.time, tz=UTC), "test_cursor")
go_to = datetime(2024, 2, 6, tzinfo=UTC)
self.bte.go_to(time=go_to)
assert self.bte.cursor.time == int(datetime.timestamp(go_to))
self.bte.reset()
@@ -72,7 +102,7 @@ class TestBackTestEngine:
assert self.bte.cursor.index == index + 3600
assert self.bte.cursor.time == int(now.timestamp()) + 3600
self.bte.setup_test_range(start=self.start, end=self.end)
assert self.bte.speed == 1
assert self.bte.speed == 60
async def test_account(self):
await self.bte.setup_account(balance=100)
@@ -122,12 +152,16 @@ class TestBackTestEngine:
async def test_get_rates_from(self):
start = datetime(2024, 2, 3, 12, 43, tzinfo=UTC)
rates = await self.bte.get_rates_from(symbol='BTCUSD', timeframe=TimeFrame.H1, date_from=start, count=24)
rates = await self.bte.get_rates_from(
symbol="BTCUSD", timeframe=TimeFrame.H1, date_from=start, count=24
)
assert len(rates) == 24
async def test_get_rates_from_2(self, bte2):
start = datetime(2024, 2, 3, 12, 12, tzinfo=UTC)
rates = await bte2.get_rates_from(symbol='BTCUSD', timeframe=TimeFrame.H1, date_from=start, count=24)
rates = await bte2.get_rates_from(
symbol="BTCUSD", timeframe=TimeFrame.H1, date_from=start, count=24
)
assert len(rates) == 24
async def test_get_rates_from_pos(self):
@@ -135,69 +169,91 @@ class TestBackTestEngine:
self.bte.go_to(time=now)
tf = TimeFrame.H2
start_pos = 2
rates = await self.bte.get_rates_from_pos(symbol='BTCUSD', timeframe=tf, start_pos=start_pos, count=24)
rates = await self.bte.get_rates_from_pos(
symbol="BTCUSD", timeframe=tf, start_pos=start_pos, count=24
)
assert len(rates) == 24
assert int(rates[-1][0]) == round_down(int(now.replace(hour = now.hour - start_pos).timestamp()), tf.seconds)
assert int(rates[-1][0]) == round_down(
int(now.replace(hour=now.hour - start_pos).timestamp()), tf.seconds
)
async def test_get_rates_from_pos2(self, bte2):
now = datetime(2024, 2, 4, 12, 15, tzinfo=UTC)
bte2.go_to(time=now)
tf = TimeFrame.H1
start_pos = 2
rates = await bte2.get_rates_from_pos(symbol='BTCUSD', timeframe=tf, start_pos=start_pos, count=24)
assert int(rates[-1][0]) == round_down(int(now.replace(hour=10).timestamp()), tf.seconds)
rates = await bte2.get_rates_from_pos(
symbol="BTCUSD", timeframe=tf, start_pos=start_pos, count=24
)
assert int(rates[-1][0]) == round_down(
int(now.replace(hour=10).timestamp()), tf.seconds
)
# assert int(rates[-1][0]) == round_up(int(now.timestamp()), tf.seconds) - start_pos * tf.seconds
assert len(rates) == 24
async def test_get_rates_range(self):
start = datetime(2024, 2, 3, 12, tzinfo=UTC)
end = datetime(2024, 2, 4, 18, tzinfo=UTC)
rates = await self.bte.get_rates_range(symbol="BTCUSD", timeframe=TimeFrame.H1, date_from=start, date_to=end)
rates = await self.bte.get_rates_range(
symbol="BTCUSD", timeframe=TimeFrame.H1, date_from=start, date_to=end
)
assert len(rates) == 31
assert int(rates[-1][0]) == int(end.timestamp())
async def test_get_rates_range2(self, bte2):
start = datetime(2024, 2, 3, 12, tzinfo=UTC)
end = datetime(2024, 2, 4, 18, tzinfo=UTC)
rates = await bte2.get_rates_range(symbol="BTCUSD", timeframe=TimeFrame.H1, date_from=start, date_to=end)
rates = await bte2.get_rates_range(
symbol="BTCUSD", timeframe=TimeFrame.H1, date_from=start, date_to=end
)
assert len(rates) == 31
assert int(rates[-1][0]) == int(end.timestamp())
async def test_get_ticks_from(self):
start = datetime(2024, 2, 3, 12, tzinfo=UTC)
ticks = await self.bte.get_ticks_from(symbol='BTCUSD', date_from=start, count=24)
ticks = await self.bte.get_ticks_from(
symbol="BTCUSD", date_from=start, count=24
)
assert len(ticks) == 24
async def test_get_ticks_from2(self, bte2):
start = datetime(2024, 2, 3, 12, tzinfo=UTC)
ticks = await bte2.get_ticks_from(symbol='BTCUSD', date_from=start, count=24)
ticks = await bte2.get_ticks_from(symbol="BTCUSD", date_from=start, count=24)
assert len(ticks) == 24
async def test_get_ticks_range(self):
start = datetime(2024, 2, 3, 12, tzinfo=UTC)
end = datetime(2024, 2, 3, 15, tzinfo=UTC)
ticks = await self.bte.get_ticks_range(symbol="BTCUSD", date_from=start, date_to=end)
approx_total = (end - start).total_seconds() // 2 # assuming 2 ticks per second at least
ticks = await self.bte.get_ticks_range(
symbol="BTCUSD", date_from=start, date_to=end
)
approx_total = (
end - start
).total_seconds() // 2 # assuming 2 ticks per second at least
assert len(ticks) >= approx_total
async def test_get_ticks_range2(self, bte2):
start = datetime(2024, 2, 3, 12, tzinfo=UTC)
end = datetime(2024, 2, 3, 15, tzinfo=UTC)
ticks = await bte2.get_ticks_range(symbol="BTCUSD", date_from=start, date_to=end)
approx_total = (end - start).total_seconds() // 2 # assuming 2 ticks per second at least
ticks = await bte2.get_ticks_range(
symbol="BTCUSD", date_from=start, date_to=end
)
approx_total = (
end - start
).total_seconds() // 2 # assuming 2 ticks per second at least
assert len(ticks) >= approx_total
async def test_price_tick(self, bte2):
moment = datetime(2024, 2, 3, 12, 12, tzinfo=UTC)
self.bte.reset()
self.bte.go_to(time=moment)
tick = await self.bte.get_price_tick(symbol='BTCUSD', time=self.bte.cursor.time)
tick = await self.bte.get_price_tick(symbol="BTCUSD", time=self.bte.cursor.time)
assert tick is not None
assert isinstance(tick.ask, float)
assert tick.ask > 0
bte2.reset()
bte2.go_to(time=moment)
tick2 = await bte2.get_price_tick(symbol='BTCUSD', time=bte2.cursor.time)
tick2 = await bte2.get_price_tick(symbol="BTCUSD", time=bte2.cursor.time)
assert tick.ask == tick2.ask
async def test_get_symbol_info(self, bte2):
@@ -206,7 +262,7 @@ class TestBackTestEngine:
self.bte.go_to(time=moment)
bte2.reset()
bte2.go_to(time=moment)
sym = 'BTCUSD'
sym = "BTCUSD"
sym_info = await self.bte.get_symbol_info(symbol=sym)
assert sym_info is not None
assert sym_info.name == sym
@@ -219,21 +275,29 @@ class TestBackTestEngine:
self.bte.go_to(time=moment)
bte2.reset()
bte2.go_to(time=moment)
sym = 'BTCUSD'
sym = "BTCUSD"
sym_info = await self.bte.get_symbol_info(symbol=sym)
dsl = (sym_info.trade_stops_level + sym_info.spread) * 2 * sym_info.point
tp = sym_info.ask + dsl
profit = await self.bte.order_calc_profit(action=OrderType.BUY, symbol=sym,
volume=sym_info.volume_min, price_open=sym_info.ask,
price_close=tp)
profit = await self.bte.order_calc_profit(
action=OrderType.BUY,
symbol=sym,
volume=sym_info.volume_min,
price_open=sym_info.ask,
price_close=tp,
)
assert profit > 0
sym_info2 = await bte2.get_symbol_info(symbol=sym)
dsl2 = (sym_info2.trade_stops_level + sym_info2.spread) * 2 * sym_info2.point
tp2 = sym_info2.ask + dsl2
profit2 = await bte2.order_calc_profit(action=OrderType.BUY, symbol=sym,
volume=sym_info2.volume_min, price_open=sym_info2.ask,
price_close=tp2)
profit2 = await bte2.order_calc_profit(
action=OrderType.BUY,
symbol=sym,
volume=sym_info2.volume_min,
price_open=sym_info2.ask,
price_close=tp2,
)
assert profit == profit2
async def test_order_margin(self, bte2):
@@ -242,14 +306,22 @@ class TestBackTestEngine:
self.bte.go_to(time=moment)
bte2.reset()
bte2.go_to(time=moment)
sym = 'BTCUSD'
sym = "BTCUSD"
sym_info = await self.bte.get_symbol_info(symbol=sym)
margin = await self.bte.order_calc_margin(action=OrderType.SELL, symbol=sym,
volume=sym_info.volume_min, price=sym_info.bid)
margin = await self.bte.order_calc_margin(
action=OrderType.SELL,
symbol=sym,
volume=sym_info.volume_min,
price=sym_info.bid,
)
assert margin > 0
sym_info2 = await self.bte.get_symbol_info(symbol=sym)
margin2 = await bte2.order_calc_margin(action=OrderType.SELL, symbol=sym,
volume=sym_info2.volume_min, price=sym_info2.bid)
margin2 = await bte2.order_calc_margin(
action=OrderType.SELL,
symbol=sym,
volume=sym_info2.volume_min,
price=sym_info2.bid,
)
assert margin2 > 0
async def test_order_check(self, buy_order, sell_order):
+7 -7
View File
@@ -20,22 +20,22 @@ class TestBaseClass:
assert "attr2=test" in repr_str
def test_set_attributes(self, child):
child.set_attributes(attr3=3.14, attr2='str')
assert child.attr2 == 'str'
assert getattr(child, 'attr3', None) is None
child.set_attributes(attr3=3.14, attr2="str")
assert child.attr2 == "str"
assert getattr(child, "attr3", None) is None
def test_annotations(self, child):
annotations = child.annotations
assert isinstance(annotations, dict)
def test_get_dict(self, child):
child.set_attributes(attr2='test')
child.set_attributes(attr2="test")
result = child.get_dict()
assert result["attr"] == 1
assert result["attr2"] == "test"
def test_get_dict_with_exclude(self, child):
child.set_attributes(attr2='test')
child.set_attributes(attr2="test")
result = child.get_dict(exclude={"attr"})
assert "attr" not in result
assert result["attr2"] == "test"
@@ -49,8 +49,8 @@ class TestBaseClass:
def test_class_vars(self, child):
class_vars = child.class_vars
assert isinstance(class_vars, dict)
assert 'cls_attr' in class_vars
assert 'attr' not in class_vars
assert "cls_attr" in class_vars
assert "attr" not in class_vars
def test_dict_property(self, child):
child.set_attributes(attr2="test")
+5 -6
View File
@@ -10,7 +10,7 @@ class TestBotFactoryAndExecutor:
def setup_class(cls):
cls.bot = Bot()
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def initialize(self):
self.bot.add_coroutine(coroutine=self.coro_one)
self.bot.add_coroutine(coroutine=self.coro_two)
@@ -20,24 +20,24 @@ class TestBotFactoryAndExecutor:
@staticmethod
def fun_one():
print('function one')
print("function one")
@staticmethod
async def coro_thread():
while True:
print('coroutine thread')
print("coroutine thread")
await asyncio.sleep(1)
@staticmethod
async def coro_one():
while True:
print('coroutine one')
print("coroutine one")
await asyncio.sleep(1)
@staticmethod
async def coro_two():
while True:
print('coroutine two')
print("coroutine two")
await asyncio.sleep(1)
def test_add_workers(self):
@@ -46,5 +46,4 @@ class TestBotFactoryAndExecutor:
# task_queue already added coroutine_thread
assert len(self.bot.executor.coroutine_threads) == 2
# def
+25 -22
View File
@@ -11,8 +11,12 @@ from aiomql.core.constants import TimeFrame
class TestCandle:
@classmethod
def setup_class(cls):
cls.bullish_candle = Candle(open=1.3421, high=1.3462, low=1.3405, close=1.3452, time=0, Index=0)
cls.bearish_candle = Candle(open=1.3452, high=1.3405, low=1.3462, close=1.3421, time=1, Index=1)
cls.bullish_candle = Candle(
open=1.3421, high=1.3462, low=1.3405, close=1.3452, time=0, Index=0
)
cls.bearish_candle = Candle(
open=1.3452, high=1.3405, low=1.3462, close=1.3421, time=1, Index=1
)
def test_repr(self):
repr_str = repr(self.bearish_candle)
@@ -33,16 +37,16 @@ class TestCandle:
def test_dict(self):
self.bearish_candle.set_attributes(ema=10)
result = self.bearish_candle.dict(exclude={'time'})
result2 = self.bearish_candle.dict(include={'close', 'high'})
assert result['open'] == 1.3452
assert result['ema'] == 10
assert 'time' not in result
assert set(result2.keys()) == {'close', 'high'}
result = self.bearish_candle.dict(exclude={"time"})
result2 = self.bearish_candle.dict(include={"close", "high"})
assert result["open"] == 1.3452
assert result["ema"] == 10
assert "time" not in result
assert set(result2.keys()) == {"close", "high"}
def test_dictionary_properties(self):
self.bearish_candle['ema'] = 4
assert self.bearish_candle['ema'] == 4
self.bearish_candle["ema"] = 4
assert self.bearish_candle["ema"] == 4
def test_candle_type(self):
assert self.bearish_candle.is_bearish()
@@ -50,16 +54,15 @@ class TestCandle:
class TestCandles:
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
async def candles(self):
mt = MetaTrader()
start = datetime(day=5, month=10, year=2023)
rates = await mt.copy_rates_from("BTCUSD", mt.TIMEFRAME_H1, start, 200)
return Candles(data=rates)
def test_get_series(self, candles):
series = candles['open']
series = candles["open"]
assert isinstance(series, pd.Series)
assert len(series) == 200
@@ -77,13 +80,13 @@ class TestCandles:
def test_setitem(self, candles):
new_series = candles.open
new_series = new_series * 2
candles['double_open'] = new_series
assert 'double_open' in candles.data.columns
candles["double_open"] = new_series
assert "double_open" in candles.data.columns
def test_getattr(self, candles):
open_series = candles.open
assert isinstance(open_series, pd.Series)
assert open_series.equals(candles.data['open'])
assert open_series.equals(candles.data["open"])
def test_iter(self, candles):
l_5 = candles[-5:]
@@ -94,13 +97,13 @@ class TestCandles:
assert tf == TimeFrame.H1
def test_ta_and_rename(self, candles):
ema = candles.ta.ema(close='open', length=10, append=True)
assert 'EMA_10' in candles.data.columns
candles.rename(inplace=True, EMA_10='ema')
assert 'ema' in candles.data.columns
ema = candles.ta.ema(close="open", length=10, append=True)
assert "EMA_10" in candles.data.columns
candles.rename(inplace=True, EMA_10="ema")
assert "ema" in candles.data.columns
def test_ta_lib(self, candles):
fas = candles.ta_lib.above(candles.open, candles.close)
assert isinstance(fas, pd.Series)
candles['fas'] = fas
assert 'fas' in candles.data.columns
candles["fas"] = fas
assert "fas" in candles.data.columns
+7 -7
View File
@@ -1,10 +1,10 @@
from aiomql.core.config import Config
from aiomql.contrib.backtesting import BackTestEngine
from aiomql.core.backtesting import BackTestEngine
class TestConfig:
def test_singleton(self, config):
config2 = Config(filename='test.json')
config2 = Config(filename="test.json")
assert config is config2
def test_set_attributes(self, config):
@@ -20,10 +20,10 @@ class TestConfig:
def test_account_info(self, config):
account_info = config.account_info()
assert isinstance(account_info, dict)
assert 'login' in account_info
assert 'password' in account_info
assert 'server' in account_info
assert "login" in account_info
assert "password" in account_info
assert "server" in account_info
def test_load_config(self, config):
config.load_config(file='tests/live/configs/test2.json')
assert config.filename == 'test2.json'
config.load_config(file="tests/live/configs/test2.json")
assert config.filename == "test2.json"
+17 -10
View File
@@ -3,7 +3,7 @@ from datetime import datetime, UTC
import pytest
from aiomql.contrib.backtesting.get_data import GetData
from aiomql.core.backtesting.get_data import GetData
from aiomql.core.constants import TimeFrame
@@ -12,12 +12,17 @@ class TestGetData:
def setup_class(cls):
cls.start = datetime(2024, 2, 1, tzinfo=UTC)
cls.end = datetime(2024, 2, 2, tzinfo=UTC)
cls.symbols = ['BTCUSD', "ETHUSD"]
cls.symbols = ["BTCUSD", "ETHUSD"]
cls.timeframes = [TimeFrame.H1, TimeFrame.H2]
cls.g_data = GetData(start=cls.start, end=cls.end, symbols=cls.symbols, timeframes=cls.timeframes,
name='test_data')
cls.g_data = GetData(
start=cls.start,
end=cls.end,
symbols=cls.symbols,
timeframes=cls.timeframes,
name="test_data",
)
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def get_data(self):
await self.g_data.get_data()
self.g_data.save_data()
@@ -27,9 +32,11 @@ class TestGetData:
assert self.g_data.end == self.end
assert self.g_data.symbols == set(self.symbols)
assert self.g_data.timeframes == set(self.timeframes)
assert self.g_data.name == 'test_data'
assert self.g_data.name == "test_data"
assert self.g_data.range == range(int((self.end - self.start).total_seconds()))
assert self.g_data.span == range(int(self.start.timestamp()), int(self.end.timestamp()))
assert self.g_data.span == range(
int(self.start.timestamp()), int(self.end.timestamp())
)
async def test_get_data(self):
assert self.g_data.data.fully_loaded is True
@@ -37,12 +44,12 @@ class TestGetData:
assert len(self.g_data.data.symbols.keys()) == 2
async def test_save_data(self):
file = Path(self.g_data.config.backtest_dir / 'test_data.pkl')
file = Path(self.g_data.config.backtest_dir / "test_data.pkl")
assert file.exists()
async def test_load_data(self):
data = GetData.load_data(name='tests/live/backtesting/test_data.pkl')
assert data.name == 'test_data'
data = GetData.load_data(name="tests/live/backtesting/test_data.pkl")
assert data.name == "test_data"
assert data.fully_loaded is True
assert len(data.ticks.keys()) == 2
assert len(data.symbols.keys()) == 2
+2 -1
View File
@@ -3,8 +3,9 @@ from datetime import datetime
import pytest
from aiomql.lib.history import History
class TestHistory:
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def init(self, make_buy_sell_orders):
await self.history.initialize()
+20 -12
View File
@@ -33,7 +33,7 @@ class TestMetaTrader:
res = await self.mt.last_error()
assert isinstance(res, tuple)
assert res[0] == 1
assert res[1] == 'Success'
assert res[1] == "Success"
async def test_version(self):
res = await self.mt.version()
@@ -111,13 +111,19 @@ class TestMetaTrader:
assert res.shape[0] == 10
async def test_copy_ticks_from(self):
res = await self.mt.copy_ticks_from(self.symbol, self.start, 10, self.mt.COPY_TICKS_ALL)
res = await self.mt.copy_ticks_from(
self.symbol, self.start, 10, self.mt.COPY_TICKS_ALL
)
assert res is not None
assert res.shape[0] == 10
async def test_copy_ticks_range(self):
res = await self.mt.copy_ticks_range(self.symbol, self.start, self.end, self.mt.COPY_TICKS_ALL)
res2 = self.mt5.copy_ticks_range(self.symbol, self.start, self.end, self.mt5.COPY_TICKS_ALL)
res = await self.mt.copy_ticks_range(
self.symbol, self.start, self.end, self.mt.COPY_TICKS_ALL
)
res2 = self.mt5.copy_ticks_range(
self.symbol, self.start, self.end, self.mt5.COPY_TICKS_ALL
)
assert res is not None
assert res.shape[0] == res2.shape[0]
@@ -132,18 +138,20 @@ class TestMetaTrader:
assert len(res) == 0
async def test_order_calc_margin(self, sell_order):
price = sell_order['price']
volume = sell_order['volume']
type_ = sell_order['type']
price = sell_order["price"]
volume = sell_order["volume"]
type_ = sell_order["type"]
res = await self.mt.order_calc_margin(type_, self.symbol, volume, price)
assert isinstance(res, float)
async def test_order_calc_profit(self, buy_order):
volume = buy_order['volume']
price_open = buy_order['price']
price_close = buy_order['tp']
type_ = buy_order['type']
res = await self.mt.order_calc_profit(type_, self.symbol, volume, price_open, price_close)
volume = buy_order["volume"]
price_open = buy_order["price"]
price_close = buy_order["tp"]
type_ = buy_order["type"]
res = await self.mt.order_calc_profit(
type_, self.symbol, volume, price_open, price_close
)
assert isinstance(res, float)
async def test_order_check(self, buy_order):
+1 -1
View File
@@ -4,7 +4,7 @@ from aiomql.lib.positions import Positions
class TestPositions:
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def init(self, make_buy_sell_orders):
await self.positions.get_positions()
+1 -1
View File
@@ -11,7 +11,7 @@ class TestRAM:
assert self.ram.min_amount <= res <= self.ram.max_amount
async def test_checks(self, buy_order, sell_order, mt):
for i in range(self.ram.open_limit+1):
for i in range(self.ram.open_limit + 1):
if i % 2 == 0:
await mt.order_send(buy_order)
else:
+9 -9
View File
@@ -7,12 +7,11 @@ from aiomql.core.models import OrderSendResult
class TestResult:
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def parameters(self):
return {'name': 'test_trades', 'ema': 20, 'rsi': 14}
return {"name": "test_trades", "ema": 20, "rsi": 14}
@pytest.fixture(scope='function')
@pytest.fixture(scope="function")
async def order_results(self, mt, sell_order, buy_order, parameters):
res1 = await mt.order_send(sell_order)
res2 = await mt.order_send(buy_order)
@@ -24,10 +23,9 @@ class TestResult:
res1, res2 = order_results
data1 = res1.get_data()
data2 = res2.get_data()
assert data1['actual_profit'] == data2['actual_profit'] == 0
assert data1['closed'] == data2['closed'] == False
assert data1['win'] == data2['win'] == False
assert data1["actual_profit"] == data2["actual_profit"] == 0
assert data1["closed"] == data2["closed"] == False
assert data1["win"] == data2["win"] == False
async def test_csv(self, order_results):
res1, res2 = order_results
@@ -38,7 +36,9 @@ class TestResult:
async def test_json(self, order_results):
res1, res2 = order_results
await asyncio.gather(res1.save(trade_record_mode='json'), res2.save(trade_record_mode='json'))
await asyncio.gather(
res1.save(trade_record_mode="json"), res2.save(trade_record_mode="json")
)
assert res1.config.records_dir.exists()
record = res1.config.records_dir / f"{res1.name}.json"
assert record.exists()
+15 -13
View File
@@ -6,25 +6,27 @@ from aiomql.lib.sessions import Session, Sessions, delta
class TestSessions:
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def make_sessions(self, make_session):
london, all_day, over_night = make_session
return Sessions(sessions=[london, all_day, over_night])
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def make_session(self):
end = time(hour=16, minute=59, second=59, microsecond=999_999, tzinfo=UTC)
london = Session(start=8, end=end, name='London', on_end='close_all')
start, end = time(hour=0, tzinfo=UTC), time(hour=23, minute=59, second=59, tzinfo=UTC)
all_day = Session(start=start, end=end, name='AllDay', on_end='close_all')
end = time(hour=16, minute=59, second=59, microsecond=999_999, tzinfo=UTC)
london = Session(start=8, end=end, name="London", on_end="close_all")
start, end = time(hour=0, tzinfo=UTC), time(
hour=23, minute=59, second=59, tzinfo=UTC
)
all_day = Session(start=start, end=end, name="AllDay", on_end="close_all")
end = time(hour=6, minute=59, second=59, microsecond=999_999, tzinfo=UTC)
over_night = Session(start=18, end=end, name='OverNight', on_end='close_all')
over_night = Session(start=18, end=end, name="OverNight", on_end="close_all")
return london, all_day, over_night
def test_session_attributes(self, make_session):
london, all_day, over_night = make_session
period = over_night.duration()
assert london.name == 'London'
assert london.name == "London"
assert london.start == time(hour=8, tzinfo=UTC)
assert london.end.hour == 16
assert period.hours == 12
@@ -55,13 +57,13 @@ class TestSessions:
no_sess = sessions.find(moment=time(hour=17, tzinfo=UTC))
mid_nite_sess = sessions.find(moment=mid_nite)
current_sess = sessions.find(moment=now)
assert current_sess.name == 'OverNight'
assert noon_sess.name == 'London'
assert current_sess.name == "OverNight"
assert noon_sess.name == "London"
assert no_sess is None
assert next_sess.name == 'London'
assert mid_nite_sess.name == 'OverNight'
assert next_sess.name == "London"
assert mid_nite_sess.name == "OverNight"
current = datetime.now(UTC).time()
if current.hour not in (7, 17):
await sessions.check()
assert sessions.current_session is not None
assert sessions.current_session.name in ('London', 'OverNight')
assert sessions.current_session.name in ("London", "OverNight")
+13 -7
View File
@@ -8,16 +8,16 @@ from aiomql.lib.ticks import Ticks
class TestSymbol:
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def btc(self):
symbol = Symbol(name='BTCUSD')
select = getattr(symbol, 'select', False)
symbol = Symbol(name="BTCUSD")
select = getattr(symbol, "select", False)
if select is False:
await symbol.initialize()
return symbol
async def test_symbol_attributes(self, btc):
assert btc.name == 'BTCUSD'
assert btc.name == "BTCUSD"
assert btc.select is True
assert btc.tick is not None
@@ -35,13 +35,19 @@ class TestSymbol:
async def test_rates(self, btc):
start = datetime(year=2023, month=10, day=5)
end = start + timedelta(hours=9)
rates_from = await btc.copy_rates_from(timeframe=btc.mt5.TIMEFRAME_H1, date_from=start, count=10)
rates_from = await btc.copy_rates_from(
timeframe=btc.mt5.TIMEFRAME_H1, date_from=start, count=10
)
assert isinstance(rates_from, Candles)
assert len(rates_from) == 10
rates_from_pos = await btc.copy_rates_from_pos(timeframe=btc.mt5.TIMEFRAME_H1, count=10, start_position=0)
rates_from_pos = await btc.copy_rates_from_pos(
timeframe=btc.mt5.TIMEFRAME_H1, count=10, start_position=0
)
assert isinstance(rates_from_pos, Candles)
assert len(rates_from_pos) == 10
rates_range = await btc.copy_rates_range(timeframe=btc.mt5.TIMEFRAME_H1, date_from=start, date_to=end)
rates_range = await btc.copy_rates_range(
timeframe=btc.mt5.TIMEFRAME_H1, date_from=start, date_to=end
)
assert isinstance(rates_range, Candles)
assert len(rates_range) == 10
ticks_from = await btc.copy_ticks_from(date_from=start, count=10)
+7 -7
View File
@@ -7,21 +7,21 @@ class TestTaskQueue:
@classmethod
def setup_class(cls):
cls.task_queue = TaskQueue(timeout=5, worker_timeout=1)
cls.data = {}
cls.data = {}
async def task_one(self):
for i in range(10):
await asyncio.sleep(0.5)
self.data.setdefault('task_one', {})[i] = f"task_one_{i}"
self.data.setdefault("task_one", {})[i] = f"task_one_{i}"
async def task_two(self):
for i in range(10):
await asyncio.sleep(0.5)
self.data.setdefault('task_two', {})[i] = f"task_two_{i}"
self.data.setdefault("task_two", {})[i] = f"task_two_{i}"
async def task_three(self):
for i in range(10):
self.data.setdefault('task_three', {})[i] = f"task_three_{i}"
self.data.setdefault("task_three", {})[i] = f"task_three_{i}"
await asyncio.sleep(10)
async def test_queue(self):
@@ -34,7 +34,7 @@ class TestTaskQueue:
assert self.task_queue.queue.qsize() == 2
self.task_queue.add(item=QueueItem(self.task_three), must_complete=False)
await self.task_queue.run()
assert len(self.data['task_one']) >= 2
assert len(self.data['task_two']) == 10
assert len(self.data['task_three']) == 1
assert len(self.data["task_one"]) >= 2
assert len(self.data["task_two"]) == 10
assert len(self.data["task_three"]) == 1
assert len(self.task_queue.priority_tasks) == 0
+1 -1
View File
@@ -4,7 +4,7 @@ from aiomql.lib.terminal import Terminal
class TestTerminal:
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def init_terminal(self):
terminal = Terminal()
init = await terminal.initialize()
+6 -5
View File
@@ -3,16 +3,17 @@ from datetime import datetime
from aiomql.lib.ticks import Ticks, Tick
from pandas import Series
class TestTicks:
async def test_tick(self, mt):
btc_tick = await mt.symbol_info_tick("BTCUSD")
btc_tick = Tick(**btc_tick._asdict())
tick_dict = btc_tick.dict(include={'ask', 'bid', 'time', 'volume'})
tick_dict = btc_tick.dict(include={"ask", "bid", "time", "volume"})
assert isinstance(btc_tick, Tick)
assert isinstance(tick_dict, dict)
assert 'ask' in tick_dict
assert 'bid' in tick_dict
assert 'volume_real' not in tick_dict
assert "ask" in tick_dict
assert "bid" in tick_dict
assert "volume_real" not in tick_dict
async def test_ticks(self, mt):
start = datetime(year=2023, month=10, day=5)
@@ -21,6 +22,6 @@ class TestTicks:
assert isinstance(ticks, Ticks)
assert len(ticks) == 10
assert isinstance(ticks[0], Tick)
bids = ticks['bid']
bids = ticks["bid"]
assert len(bids) == 10
assert isinstance(bids, Series)
+17 -9
View File
@@ -11,10 +11,10 @@ class TestTrader:
@classmethod
def setup_class(cls):
ram = RAM(fixed_amount=10)
cls.trader = SimpleTrader(symbol=ForexSymbol(name='BTCUSD'), ram=ram)
cls.simple_trader2 = SimpleTrader(symbol=ForexSymbol(name='EURJPY'), ram=ram)
cls.trader = SimpleTrader(symbol=ForexSymbol(name="BTCUSD"), ram=ram)
cls.simple_trader2 = SimpleTrader(symbol=ForexSymbol(name="EURJPY"), ram=ram)
@pytest.fixture(scope='class', autouse=True)
@pytest.fixture(scope="class", autouse=True)
async def initialize(self):
await self.trader.symbol.initialize()
await self.simple_trader2.symbol.initialize()
@@ -27,22 +27,26 @@ class TestTrader:
assert res.retcode == 10009
async def test_create_order_with_sl(self):
sl = (self.trader.symbol.trade_stops_level * 2 + self.trader.symbol.spread) * self.trader.symbol.point
sl = (
self.trader.symbol.trade_stops_level * 2 + self.trader.symbol.spread
) * self.trader.symbol.point
tick = await self.trader.symbol.info_tick()
sl = tick.bid + sl
await self.trader.create_order_with_sl(order_type=OrderType.SELL, sl=sl)
res = await self.trader.order.send()
profit = floor(await self.trader.order.calc_profit())
loss = -floor(abs(await self.trader.order.calc_loss()))
assert profit == -loss*self.trader.ram.risk_to_reward
assert profit == -loss * self.trader.ram.risk_to_reward
assert profit == self.trader.ram.fixed_amount * self.trader.ram.risk_to_reward
assert loss == -self.trader.ram.fixed_amount
assert res is not None
assert res.retcode == 10009
async def test_create_order_with_points(self):
points = (self.trader.symbol.trade_stops_level * 2 + self.trader.symbol.spread)
await self.trader.create_order_with_points(order_type=OrderType.BUY, points=points)
points = self.trader.symbol.trade_stops_level * 2 + self.trader.symbol.spread
await self.trader.create_order_with_points(
order_type=OrderType.BUY, points=points
)
res = await self.trader.order.send()
profit = floor(await self.trader.order.calc_profit())
loss = -floor(abs(await self.trader.order.calc_loss()))
@@ -53,12 +57,16 @@ class TestTrader:
assert res.retcode == 10009
async def test_create_order_with_stops(self):
sl = (self.trader.symbol.trade_stops_level * 2 + self.trader.symbol.spread) * self.trader.symbol.point
sl = (
self.trader.symbol.trade_stops_level * 2 + self.trader.symbol.spread
) * self.trader.symbol.point
tp = sl * self.trader.ram.risk_to_reward
tick = await self.trader.symbol.info_tick()
sl = tick.ask - sl
tp = tick.ask + tp
await self.trader.create_order_with_stops(order_type=OrderType.BUY, sl=sl, tp=tp)
await self.trader.create_order_with_stops(
order_type=OrderType.BUY, sl=sl, tp=tp
)
res = await self.trader.order.send()
profit = floor(await self.trader.order.calc_profit())
loss = -floor(abs(await self.trader.order.calc_loss()))