This commit is contained in:
Ichinga Samuel
2025-07-20 17:07:55 +01:00
parent 4178de2a0b
commit 675dbc95a2
55 changed files with 1413 additions and 234 deletions
View File
+21
View File
@@ -0,0 +1,21 @@
import logging
from aiomql import Bot, ForexSymbol, auto_commit, OpenPositionsTracker
from .emaxover import EMAXOver
logging.basicConfig(level=logging.INFO)
def x_bot():
syms = ["LTCUSD", "ETHUSD", "SOLUSD", "BTCUSD"]
symbols = [ForexSymbol(name=sym) for sym in syms]
strategies = [EMAXOver(symbol=symbol) for symbol in symbols]
bot = Bot()
bot.add_strategies(strategies=strategies)
bot.add_coroutine(coroutine=OpenPositionsTracker(autocommit=True).track, on_separate_thread=True)
# bot.add_coroutine(coroutine=auto_commit, on_separate_thread=True)
bot.execute()
x_bot()
@@ -1,5 +1,6 @@
from aiomql import Strategy, ForexSymbol, TimeFrame, Tracker, OrderType, Sessions, Trader, ScalpTrader
from .traders import TestTrader
class EMAXOver(Strategy):
ttf: TimeFrame # time frame for the strategy
@@ -12,18 +13,18 @@ class EMAXOver(Strategy):
# default parameters for the strategy
# they are set as attributes. You can override them in the constructor via the params argument.
parameters = {'ttf': TimeFrame.H1, 'tcc': 3000, 'fast_ema': 34, 'slow_ema': 55, 'interval': TimeFrame.M15,
'timeout': 3 * 60 * 60}
parameters = {'ttf': TimeFrame.M10, 'tcc': 3000, 'fast_ema': 34, 'slow_ema': 55, 'interval': TimeFrame.M5,
'timeout': 120}
def __init__(self, *, symbol: ForexSymbol, params: dict | None = None, trader: Trader = None,
sessions: Sessions = None, name: str = "EMAXOver"):
super().__init__(symbol=symbol, params=params, sessions=sessions, name=name)
self.tracker = Tracker(snooze=self.interval.seconds)
self.trader = trader or ScalpTrader(symbol=self.symbol)
self.trader = trader or TestTrader(symbol=self.symbol)
async def find_entry(self):
# get the candles
candles = await self.symbol.copy_rates_from_pos(timeframe=self.ttf, start_position=0, count=self.tcc)
candles = await self.symbol.copy_rates_from_pos(timeframe=self.ttf, count=self.tcc)
# get the fast moving average
candles.ta.ema(length=self.fast_ema, append=True)
@@ -34,9 +35,9 @@ class EMAXOver(Strategy):
# check for crossovers
# fast above slow
fas = candles.ta_lib.cross(candles.fast_ema, candles.slow_ema, above=True)
fas = candles.ta_lib.above(candles.fast_ema, candles.slow_ema)
# fast below slow
fbs = candles.ta_lib.cross(candles.fast_ema, candles.slow_ema, above=False)
fbs = candles.ta_lib.below(candles.fast_ema, candles.slow_ema)
## check for entry signals in the current candle
if fas.iloc[-1]:
+1
View File
@@ -0,0 +1 @@
from .track import close_after
+18
View File
@@ -0,0 +1,18 @@
from logging import getLogger
from datetime import datetime
from aiomql import OpenPosition
logger = getLogger(__name__)
async def close_after(open_pos: OpenPosition, /, *, duration: int, start: float = 0):
if not await open_pos.update_position():
return
pos = open_pos.position
start = start or pos.time
diff = datetime.now().timestamp() - start
if diff > duration:
_, res = await open_pos.close_position()
if res.retcode == 10009:
logger.info("%s, %d closed", pos.symbol, pos.ticket)
+1
View File
@@ -0,0 +1 @@
from .test_trader import TestTrader
@@ -0,0 +1,42 @@
from logging import getLogger
from datetime import datetime
from aiomql import Trader, OrderType, OpenPosition, Positions, PositionTracker, Store
from ..trackers import close_after
logger = getLogger(__name__)
class TestTrader(Trader):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.positions = Positions()
self.store = Store()
async def place_trade(self, *, order_type: OrderType, volume: float = None, parameters: dict = None):
"""Places a trade based on the order_type and volume. The volume is optional. If not provided, the minimum volume
for the symbol will be used. This trade is placed without a stop_loss or take_profit. The trade is recorded in the
trade_record file.
Args:
order_type (OrderType): The order_type
volume (float): The volume to trade
parameters (dict): Parameters associated with the trade
"""
try:
self.parameters |= parameters or {}
volume = volume or self.symbol.volume_min
await self.create_order_no_stops(order_type=order_type, volume=volume)
if not await self.check_order():
return
self.order.comment = self.parameters.get("name", self.__class__.__name__)
res = await self.send_order()
if res is not None and res.retcode == 10009:
position = await self.positions.get_position_by_ticket(ticket=res.order)
open_position = OpenPosition(ticket=res.order, symbol=self.symbol, position=position)
kwargs = {"duration": 40, "start": datetime.now().timestamp()}
ca = PositionTracker(close_after, **kwargs)
open_position.add_tracker(tracker=ca)
await self.record_trade(result=res, parameters=self.parameters)
except Exception as err:
logger.error(f"{err} in {self.__class__.__name__}.place_trade for {self.symbol.name}")
-19
View File
@@ -1,19 +0,0 @@
import logging
from aiomql import Bot, ForexSymbol
from emaxover import EMAXOver
logging.basicConfig(level=logging.INFO)
def x_bot():
syms = ["EURUSD", "GBPUSD", "USDJPY"]
symbols = [ForexSymbol(name=sym) for sym in syms]
strategies = [EMAXOver(symbol=symbol) for symbol in symbols]
bot = Bot()
bot.add_strategies(strategies=strategies)
bot.execute()
x_bot()