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
+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)