verion 3.0.4

This commit is contained in:
Ichinga Samuel
2023-10-17 05:21:43 +01:00
parent e285610fe5
commit 21043dd326
11 changed files with 167 additions and 3753 deletions
+47 -3571
View File
File diff suppressed because it is too large Load Diff
+11 -9
View File
@@ -8,14 +8,16 @@ class Session()
A session is a time period between two datetime.time objects specified in utc.
### Attributes:
|Name|Type|Description|Default|
|---|---|---|---|
|**start**|**datetime.time**|The start time of the session.|None|
|**end**|**datetime.time**|The end time of the session.|None|
|**on_start**|**str**|The action to take when the session starts. Default is None.|None|
|**on_end**|**str**|The action to take when the session ends. Default is None.|None|
|**custom_start**|**Callable**|A custom function to call when the session starts. Default is None.|None|
|**custom_end**|**Callable**|A custom function to call when the session ends. Default is None.|None|
|Name| Type | Description | Default |
|---|----------------|------------------------------------------------------------------------|----|
|**start**| **datetime.time** | The start time of the session. | None |
|**end**| **datetime.time** | The end time of the session. | None |
|**on_start**| **Literal['close_all', 'close_win', 'close_loss', 'custom_start']** | The action to take when the session starts. Default is None. | None |
|**on_end**| **Literal['close_all', 'close_win', 'close_loss', 'custom_end']** | The action to take when the session ends. Default is None. | None |
|**custom_start**| **Callable** | A custom function to call when the session starts. Default is None. | None |
|**custom_end**| **Callable** | A custom function to call when the session ends. Default is None. | None |
|**name**| **str** | The name of the session. Default is a combination of start and finish. | |
|**seconds**| **set[int]** | The set of seconds in the session. | None |
### Methods:
|Name|Description|
@@ -23,7 +25,6 @@ A session is a time period between two datetime.time objects specified in utc.
|**begin**|Call the action specified in on_start or custom_start.|
|**close**|Call the action specified in on_end or custom_end.|
|**action**|Used by begin and close to call the action specified.|
|**delta**|Get the timedelta of a datetime.time object.|
|**until**|Get the seconds until the session starts from the current time.|
### \_\_init\_\_
@@ -49,6 +50,7 @@ Create a session.
|**on_end**| **Literal['close_all', 'close_win', 'close_loss', 'custom_end']** | The action to take when the session ends. Default is None. | None |
|**custom_start**| **Callable** | A custom function to call when the session starts. Default is None. | None |
|**custom_end**| **Callable** | A custom function to call when the session ends. Default is None. | None |
|**name**| **str** | The name of the session. Default is None. | None |
### begin
```python
+17 -5
View File
@@ -162,11 +162,12 @@ This is a dummy method that returns the minimum volume of the symbol. It is mean
Checkout Forex Symbol implementation in [ForexSymbol](#forexsymbol)
#### Arguments:
|Name| Type | Description | Default |
|---|--------------------|-----------------------------|-------------------|
|**amount**| **float** | Amount to risk in the trade | None |
|**pips**| **float** | Number of pips to target | None |
|**use_minimum**| **bool** | If True, the minimum volume is returned if the computed volume is less than the minimum volume. | True |
| Name | Type | Description | Default |
|----------------|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| **amount** | **float** | Amount to risk in the trade | None |
| **pips** | **float** | Number of pips to target | None |
| **use_limits** | **bool** | If True, the minimum volume is returned if the computed volume is less than the minimum volume and the maximum volume is returned if the computed volume is greater than the maximum volume for the symbol | False |
#### Returns:
|Type|Description|
|---|---|
@@ -308,3 +309,14 @@ Get ticks for the specified date range from the MetaTrader 5 terminal.
|Exception|Description|
|---|---|
|**ValueError**|If request was unsuccessful and None was returned|
### compute_volume
```python
async def compute_volume(*,
amount: float,
pips: float,
use_limits: bool = True) -> float
```
Computes the volume of a trade based on the amount and the number of pips to target.
This is a dummy method that returns the minimum volume of the symbol. It is meant to be overridden by a subclass
Checkout Forex Symbol implementation in [ForexSymbol](#forexsymbol)
-26
View File
@@ -1,26 +0,0 @@
import logging
from aiomql.lib import FingerTrap
from aiomql import Bot, Account, ForexSymbol
logging.basicConfig(level=logging.INFO)
def build_bot():
# Either initialize an account here with your login details here or set them in the aiomql.json file.
# acc = Account(login=1111111111, password='*******', server='Deriv-Demo')
bot = Bot()
# Prebuilt strategy from the library.
# Disclaimer: These strategy is only for demonstration purposes.
params = {'trend_candles_count': 500}
st1 = FingerTrap(symbol=ForexSymbol(name='GBPUSD'), params=params)
st3 = FingerTrap(symbol=ForexSymbol(name='AUDUSD'), params=params)
st4 = FingerTrap(symbol=ForexSymbol(name='USDCAD'), params=params)
st5 = FingerTrap(symbol=ForexSymbol(name='USDJPY'), params=params)
st6 = FingerTrap(symbol=ForexSymbol(name='EURGBP'), params=params)
bot.add_strategies([st1, st3, st4, st5, st6])
bot.execute()
build_bot()
+38
View File
@@ -0,0 +1,38 @@
from datetime import time
import logging
from aiomql.lib import FingerTrap
from aiomql import Bot, Account, ForexSymbol, Session, Sessions
logging.basicConfig(level=logging.INFO)
def build_bot():
# Either initialize an account here with your login details here or set them in the aiomql.json file.
# acc = Account(login=1234567, password='*******', server='Broker-Server')
bot = Bot()
# Prebuilt strategy from the library.
# Disclaimer: These strategy is only for demonstration purposes.
# The author of this library is not responsible for any losses incurred from using this strategy.
# using trade sessions is optional. the strategy will run with a default session of 24 hours if not specified.
# session start and end times are in UTC. Make sure to convert to UTC if you are in a different timezone.
# sessions can be used to close positions at the end of a trading session.
sess = Session(name='London', start=8, end=time(hour=15, minute=30), on_end='close_all')
sess2 = Session(name='New York', start=13, end=time(hour=20, minute=30))
sess3 = Session(name='Tokyo', start=23, end=time(hour=6, minute=30))
sessions = Sessions(sess, sess2, sess3)
# configurable parameters for the strategy
params = {'trend_candles_count': 500, 'fast_period': 8}
st1 = FingerTrap(symbol=ForexSymbol(name='GBPUSD'), params=params, sessions=sessions)
st3 = FingerTrap(symbol=ForexSymbol(name='AUDUSD'), params=params, sessions=sessions)
st4 = FingerTrap(symbol=ForexSymbol(name='USDCAD'), params=params, sessions=sessions)
st5 = FingerTrap(symbol=ForexSymbol(name='USDJPY'), params=params, sessions=sessions)
st6 = FingerTrap(symbol=ForexSymbol(name='EURGBP'), params=params, sessions=sessions)
bot.add_strategies([st1, st3, st4, st5, st6])
bot.execute()
build_bot()
-47
View File
@@ -1,47 +0,0 @@
from concurrent.futures import ThreadPoolExecutor
import asyncio
import random
import time
se = set()
def fun(arg):
while True:
time.sleep(10)
print('sleep')
# print('sleeping')
# await asyncio.sleep(random.randint(1, 10))
# print('wake up')
def nuf():
while True:
# time.sleep(5)
print('awake')
def main(f):
asyncio.run(f())
async def run():
# loop = asyncio.get_running_loop()
with ThreadPoolExecutor(max_workers=10) as exe:
exe.submit(fun, 10)
exe.submit(nuf)
# r.cancel()
# print(r.done())
# return r.result()
asyncio.run(run())
# ars = (1, 3, 4)
#
# def check(f, args):
# print(*args)
#
#
# b = {check: (3, ars)}
# [k(v[0], v[1]) for k,v in b.items()]
+49 -31
View File
@@ -10,6 +10,21 @@ from .positions import Positions
logger = getLogger(__name__)
def delta(obj: time):
"""Get the timedelta of a datetime.time object.
Args:
obj (datetime.time): A datetime.time object.
"""
return timedelta(hours=obj.hour, minutes=obj.minute, seconds=obj.second, microseconds=obj.microsecond)
def seconds(start: time, end: time) -> set[int]:
if start > end:
return set(range(delta(start).seconds, 86400)) | set(range(0, delta(end).seconds))
return set(range(delta(start).seconds, delta(end).seconds))
class Session:
"""A session is a time period between two datetime.time objects specified in utc.
@@ -20,6 +35,8 @@ class Session:
on_end (str): The action to take when the session ends. Default is None.
custom_start (Callable): A custom function to call when the session starts. Default is None.
custom_end (Callable): A custom function to call when the session ends. Default is None.
name (str): A name for the session. Default is a combination of start and end.
seconds (set[int]): A set of seconds between start and end.
Methods:
begin: Call the action specified in on_start or custom_start.
@@ -31,7 +48,7 @@ class Session:
def __init__(self, *, start: int | time, end: int | time,
on_start: Literal['close_all', 'close_win', 'close_loss', 'custom_start'] = None,
on_end: Literal['close_all', 'close_win', 'close_loss', 'custom_end'] = None,
custom_start: Callable = None, custom_end: Callable = None):
custom_start: Callable = None, custom_end: Callable = None, name: str = ''):
"""Create a session.
Keyword Args:
@@ -43,21 +60,28 @@ class Session:
ends. Default is None.
custom_start (Callable): A custom function to call when the session starts. Default is None.
custom_end (Callable): A custom function to call when the session ends. Default is None.
name (str): A name for the session. Default is a combination of start and end.
"""
self.start = start if isinstance(start, time) else time(hour=start)
self.end = end if isinstance(end, time) else time(hour=end)
self.__from = self.delta(self.start)
self.__to = self.delta(self.end)
self.on_start = on_start
self.on_end = on_end
self.custom_start = custom_start
self.custom_end = custom_end
self.name = name or f'{self.start} - {self.end}'
self.seconds = seconds(self.start, self.end)
def __contains__(self, item: time):
return self.start <= item < self.end
return delta(item).seconds in self.seconds
def __str__(self):
return f'{self.start}-->{self.name}-->{self.end}' if self.name else f'{self.start}-->{self.end}'
def __repr__(self):
return f'{self.start}<-{len(self)}->{self.end}'
return f'{self.start}-->{self.end}'
def __len__(self):
return (delta(self.start) - delta(self.end)).seconds
async def begin(self):
"""Call the action specified in on_start or custom_start."""
@@ -111,21 +135,9 @@ class Session:
except Exception as exe:
logger.warning(f'Failed to call action {action} due to {exe}')
@staticmethod
def delta(obj: time):
"""Get the timedelta of a datetime.time object.
Args:
obj (datetime.time): A datetime.time object.
"""
return timedelta(hours=obj.hour, minutes=obj.minute, seconds=obj.second, microseconds=obj.microsecond)
def __len__(self):
return (self.__to - self.__from).seconds
def until(self):
"""Get the seconds until the session starts from the current time."""
return (self.__from - self.delta(datetime.utcnow().time())).seconds
"""Get the seconds until the session starts from the current time in seconds."""
return (delta(self.start) - delta(datetime.utcnow().time())).seconds
class Sessions:
@@ -143,8 +155,8 @@ class Sessions:
"""
def __init__(self, *sessions: Session):
self.sessions = list(sessions)
self.sessions.sort(key=lambda x: x.start)
self.current_session = sessions[0]
self.sessions.sort(key=lambda x: (x.start, x.end))
self.current_session = None
def find(self, obj: time) -> Session | None:
"""Find a session that contains a datetime.time object.
@@ -172,7 +184,7 @@ class Sessions:
for session in self.sessions:
if obj < session.start:
return session
return self.sessions[-1]
return self.sessions[0]
def __contains__(self, item: time):
return True if self.find(item) is not None else False
@@ -187,15 +199,21 @@ class Sessions:
async def check(self):
"""Check if the current session has started and if not, wait until it starts."""
now = datetime.utcnow().time()
if now in self.current_session:
return
await self.current_session.close()
current_session = self.find(now)
if current_session is None:
current_session = self.find_next(now)
secs = current_session.until() + 10
print(f'sleeping for {secs} seconds until next session')
await sleep(secs)
if current_session:
if self.current_session:
if self.current_session == current_session:
return
await self.current_session.close()
self.current_session = current_session
await self.current_session.begin()
return
await self.current_session.close() if self.current_session else ...
current_session = self.find_next(now)
secs = current_session.until() + 10
print(f'sleeping for {secs} seconds until next {current_session} session')
await sleep(secs)
self.current_session = current_session
await self.current_session.begin()
+3 -5
View File
@@ -135,17 +135,15 @@ class Symbol(SymbolInfo):
"""
return await self.mt5.market_book_release(self.name)
async def compute_volume(self, *, amount: float, pips: float, use_minimum: bool = True) -> float:
async def compute_volume(self, *, amount: float, pips: float, use_limits: bool = True) -> float:
"""Computes the volume of a trade based on the amount and the number of pips to target.
This is a dummy method that returns the minimum volume of the symbol. It is meant to be overridden by a subclass
Checkout Forex Symbol implementation in src\aiomql\lib\ForexSymbol.py
that implements the computation of volume.
Args:
amount (float): Amount to risk in the trade
pips (float): Number of pips to target
Keyword Args:
use_minimum (bool): If True, the minimum volume is returned if the computed volume is less than the minimum volume.
use_limits (bool): If True, the computed volume is rounded to the nearest step and checked against
Returns:
float: Returns the volume of the trade
+2 -2
View File
@@ -54,7 +54,7 @@ class Trader:
"""
# check if pips is passed in as a keyword argument, if not use the pips attribute of the ram instance
pips = kwargs.get('pips', 0) or self.ram.pips
self.order.volume = self.ram.volume or await self.ram.get_volume(symbol=self.symbol, pips=pips)
self.order.volume = kwargs.get('volume', self.ram.volume) or await self.ram.get_volume(symbol=self.symbol, pips=pips)
self.order.type = order_type
await self.set_order_limits(pips=pips)
@@ -82,7 +82,7 @@ class Trader:
Args:
order_type (OrderType): Type of order
params: parameters to be saved with the trade
params: parameters of the trading strategy used to place the trade
kwargs: keyword arguments as required for the specific trader
"""
try:
-29
View File
@@ -1,29 +0,0 @@
import asyncio
from datetime import datetime
from zoneinfo import ZoneInfo
import pytz
from aiomql import MetaTrader, Symbol, TimeFrame, Account, ForexSymbol
async def main():
async with Account() as account:
d = datetime.now()
start = d.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=pytz.timezone('UTC'))
print(start.timestamp())
end = d.replace(hour=7, minute=0, second=0, microsecond=0, tzinfo=pytz.timezone('UTC'))
# print(end.timestamp())
s = ForexSymbol(name='EURUSD')
s1 = ForexSymbol(name='USDJPY')
await s.init()
await s1.init()
# t = await s.copy_ticks_range(date_from=start, date_to=end)
# t1 = await s1.copy_ticks_range(date_from=start, date_to=end)
r = await s1.copy_rates_range(date_from=start, date_to=end, timeframe=TimeFrame.M1)
# rc = await s1.copy_rates_from(date_from=end, timeframe=TimeFrame.M15, count=96)
print(datetime.fromtimestamp(r[0].time, tz=pytz.timezone('UTC')), datetime.fromtimestamp(r[-1].time), len(r))
# f = await s.copy_ticks_range(date_from=start, date_to=end)
# print(datetime.fromtimestamp(f[0].time), datetime.fromtimestamp(f[-1].time), len(f))
# print(len(f), len(rc), rc[50].open)
# print(len(r), r[-1].time, len(rc), rc[-1].time - rc[-2].time, end.timestamp(),rc[0].time)
asyncio.run(main())
-28
View File
@@ -1,28 +0,0 @@
# import asyncio
# from pprint import pprint as pp
# from aiomql import Order, Account, ForexSymbol, RAM, OrderType
#
#
# async def test_send_order():
# await Account().sign_in()
# symbol = ForexSymbol(name='Volatility 50 Index')
# await symbol.init()
# tick = await symbol.info_tick()
# pips = 100
# volume = await symbol.compute_volume(amount=100, pips=pips)
# sls = symbol.trade_stops_level
# cls = pips * symbol.pip
# print(sls, cls)
# sl = tick.ask - (cls)
# tp = tick.ask + (cls)
# sls = symbol.trade_stops_level
# po = sls * symbol.point
# pi = po * 10
# # print(sls, po, pi, sl)
# print(volume)
# order = Order(symbol=symbol.name, type=OrderType.BUY, sl=sl, tp=tp, volume=volume, price=tick.ask)
# res = await order.send()
# pp(res.dict)
asyncio.run(test_send_order())