This commit is contained in:
Ichinga Samuel
2024-08-29 06:36:09 +01:00
parent 24eec96fae
commit a62b24ac1c
6 changed files with 238 additions and 143 deletions
+61 -105
View File
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 1,
"id": "f4500c8d-0e58-4d3f-8dd3-06a4896f397f",
"metadata": {},
"outputs": [],
@@ -41,9 +41,52 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 3,
"id": "2c598a85-1e90-49b0-abf1-87329597feae",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sym = Symbol(name='Volatility 25 Index')\n",
"await sym.init()"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "0a7d319c-5760-4058-994f-27e8d10df108",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.5"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"await sym.mt5.order_calc_margin(0, 'Volatility 25 Index', 1, sym.tick.ask)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "4ec498ea-5e4e-4ff6-85da-051acc2eaf28",
"metadata": {},
"outputs": [
{
"data": {
@@ -51,136 +94,49 @@
"4"
]
},
"execution_count": 19,
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tz = pytz.timezone('Etc/UTC')\n",
"sym = Symbol(name='EURUSD')\n",
"start = datetime(day=22, month=8, year=2024, tzinfo=tz)\n",
"end = datetime(day=26, month=8, year=2024, hour=12, tzinfo=tz)\n",
"# rates = await sym.mt5.copy_rates_from(symbol='EURUSD', date_from=end, count=5, timeframe=TimeFrame.H12)\n",
"rates = await sym.mt5.copy_rates_from_pos(symbol='EURUSD', start_pos=0, count=5, timeframe=TimeFrame.H12)\n",
"df = DataFrame(rates)\n",
"df['time'] = pd.to_datetime(df['time'], unit='s')\n",
"df.index[-1]"
"sy = await sym.mt5.symbol_info('Volatility 25 Index')\n",
"await sym.mt5.symbol_select('Volatility 25 Index', enable=True)\n",
"sy.trade_calc_mode"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "0a7d319c-5760-4058-994f-27e8d10df108",
"execution_count": 12,
"id": "9307e3df-a5f3-42cf-9d3d-e65e5254c0e5",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>time</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>tick_volume</th>\n",
" <th>spread</th>\n",
" <th>real_volume</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2024-08-25 12:00:00</td>\n",
" <td>1.11869</td>\n",
" <td>1.11947</td>\n",
" <td>1.11849</td>\n",
" <td>1.11894</td>\n",
" <td>4570</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2024-08-26 00:00:00</td>\n",
" <td>1.11894</td>\n",
" <td>1.12016</td>\n",
" <td>1.11628</td>\n",
" <td>1.11652</td>\n",
" <td>43583</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2024-08-26 12:00:00</td>\n",
" <td>1.11652</td>\n",
" <td>1.11790</td>\n",
" <td>1.11501</td>\n",
" <td>1.11618</td>\n",
" <td>45034</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" time open high low close tick_volume \\\n",
"2 2024-08-25 12:00:00 1.11869 1.11947 1.11849 1.11894 4570 \n",
"3 2024-08-26 00:00:00 1.11894 1.12016 1.11628 1.11652 43583 \n",
"4 2024-08-26 12:00:00 1.11652 1.11790 1.11501 1.11618 45034 \n",
"\n",
" spread real_volume \n",
"2 1 0 \n",
"3 0 0 \n",
"4 0 0 "
"0.50097375"
]
},
"execution_count": 30,
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.loc[2:6]"
"# margin\n",
"# worked for forex.\n",
"tcs = 1 * sym.trade_contract_size\n",
"lv = Account().leverage /0.125\n",
"tcs * sym.tick.ask / lv"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9307e3df-a5f3-42cf-9d3d-e65e5254c0e5",
"id": "e8413f70-244f-47a2-97b3-2be6fcd87589",
"metadata": {},
"outputs": [],
"source": [
"now = datetime.now()\n",
"st = now.replace(hour=0, minute=0, second=0, day=1, month=1, year=2023)\n",
"et = now.replace(hour=9, minute=0, second=0)\n",
"diff = et - st\n",
"secs = int(diff.total_seconds())\n",
"# st = now.replace(hour=0, day=16)\n",
"# et = now.replace(hour=9)\n",
"# start = now.replace(day=now.day-3, tzinfo=tz)\n",
"# end = now.replace(day=now.day-1, tzinfo=tz)"
]
"source": []
},
{
"cell_type": "code",
@@ -658,7 +614,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
"version": "3.11.6"
}
},
"nbformat": 4,
+76 -6
View File
@@ -14,7 +14,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 2,
"id": "8d39819f-2cac-437f-b5fc-633ca7443f8a",
"metadata": {},
"outputs": [],
@@ -24,23 +24,93 @@
},
{
"cell_type": "code",
"execution_count": 32,
"execution_count": 6,
"id": "d7976bb8-05cb-4924-a6e2-90ea8af85d9d",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>0</th>\n",
" <th>1</th>\n",
" <th>2</th>\n",
" <th>symbols</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10</td>\n",
" <td>10</td>\n",
" <td>20</td>\n",
" <td>A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>20</td>\n",
" <td>11</td>\n",
" <td>22</td>\n",
" <td>A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>30</td>\n",
" <td>12</td>\n",
" <td>24</td>\n",
" <td>A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>40</td>\n",
" <td>13</td>\n",
" <td>26</td>\n",
" <td>A</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>50</td>\n",
" <td>14</td>\n",
" <td>28</td>\n",
" <td>A</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
"[75, 75, 75, 75, 75, 75, 75, 75, 75, 75]"
" 0 1 2 symbols\n",
"0 10 10 20 A\n",
"1 20 11 22 A\n",
"2 30 12 24 A\n",
"3 40 13 26 A\n",
"4 50 14 28 A"
]
},
"execution_count": 32,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[75]*10"
"rs[rs.symbols == 'A']"
]
},
{
@@ -365,7 +435,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
"version": "3.11.6"
}
},
"nbformat": 4,
+6 -9
View File
@@ -6,24 +6,21 @@ from logging import getLogger
import asyncio
import pytz
from MetaTrader5 import Tick, SymbolInfo
import pandas as pd
from pandas import DataFrame
from ...core.meta_trader import MetaTrader
from ...core.config import Config
from ...core.errors import Error
from ...core.constants import TimeFrame, CopyTicks, OrderType
from ...core.models import (AccountInfo, SymbolInfo, BookInfo, TradeOrder, OrderCheckResult, OrderSendResult,
TradePosition, TradeDeal, TickInfo)
from ...core.constants import TimeFrame, CopyTicks
from ...utils import backoff_decorator
logger = getLogger(__name__)
class Data(TypedDict):
account: AccountInfo
symbols: dict[str, SymbolInfo]
account: dict
symbols: dict[str, dict]
prices: dict[str, DataFrame]
ticks: dict[str, DataFrame]
rates: dict[str, dict[str, DataFrame]]
@@ -93,11 +90,11 @@ class GetData:
return data
async def get_symbols_info(self) -> dict[str, SymbolInfo]:
async def get_symbols_info(self) -> dict[str, dict]:
""""""
tasks = [self.get_symbol_info(symbol) for symbol in self.symbols]
res = await asyncio.gather(*tasks)
return {symbol: SymbolInfo(**info) for symbol, info in res}
return {symbol: info for symbol, info in res}
async def get_symbols_ticks(self) -> dict[str, DataFrame]:
""""""
+92 -15
View File
@@ -1,11 +1,12 @@
from datetime import datetime, tzinfo
from typing import Literal
import pytz
import numpy as np
import pandas as pd
from pandas import DataFrame
from MetaTrader5 import Tick, SymbolInfo, AccountInfo, TradeOrder, TradePosition, TradeDeal
import MetaTrader5
from MetaTrader5 import (Tick, SymbolInfo, AccountInfo, TradeOrder, TradePosition, TradeDeal,
ORDER_TYPE_BUY, ORDER_TYPE_SELL)
from ..constants import TimeFrame, CopyTicks
from .get_data import Data, GetData
@@ -20,8 +21,8 @@ class TestData:
def __init__(self, data: Data):
self._data = data
self.account = data['account']
self.symbols = data['symbols']
self.account = AccountInfo(**data['account'])
self.symbols = {symbol: SymbolInfo(**info) for symbol, info in data['symbols'].items()}
self.prices = data['prices']
self.ticks = data['ticks']
self.rates = data['rates']
@@ -31,8 +32,7 @@ class TestData:
self.orders: dict[str, dict[int, TradeOrder]] = {}
self.open_orders: dict[int, TradeOrder] = {}
self.positions: dict[str, dict[int, TradePosition]] = {}
self.open_positions = dict[int, TradePosition] = {}
self.history_deals = dict[str, dict[int, TradeDeal]] = {}
self.open_positions: dict[int, TradePosition] = {}
def __next__(self):
self.cursor = next(self.iter)
@@ -49,7 +49,7 @@ class TestData:
return list(self.symbols.keys())
def get_account_info(self) -> AccountInfo:
return AccountInfo(**self.account.dict)
return AccountInfo(**self.account._asdict())
def get_symbol_info_tick(self, symbol: str) -> Tick:
tick = self.prices[symbol].iloc[self.cursor]
@@ -58,7 +58,7 @@ class TestData:
def get_symbol_info(self, symbol: str) -> SymbolInfo:
info = self.symbols[symbol]
tick = self.get_symbol_info_tick(symbol)
info = info.dict
info = info._asdict()
info |= {'bid': tick.bid, 'bidhigh': tick.bid, 'bidlow': tick.bid, 'ask': tick.ask,
'askhigh': tick.ask, 'asklow': tick.bid, 'last': tick.last, 'volume_real': tick.volume_real}
return SymbolInfo(**info)
@@ -103,26 +103,103 @@ class TestData:
end = int(datetime.timestamp(date_to)) if isinstance(date_to, datetime) else int(date_to)
end = ticks[ticks.index >= end].iloc[-1].index
return ticks.loc[start:end].to_numpy()
def order_calc_margin(self, action: Literal[0, 1], symbol: str, volume: float, price: float):
symbol_info = self.get_symbol_info(symbol)
margin_rate = symbol_info.margin_rate
margin = volume * price / margin_rate
return margin
def order_send(self, request: dict) -> dict:
...
def order_check(self, request: dict) -> dict:
...
def get_orders_total(self) -> int:
return len(self.live_orders)
return len(self.open_orders)
def get_orders(self, symbol: str = '', group: str = '', ticket: int = None) -> tuple[TradeOrder, ...]:
if ticket:
return self.live_orders.get(ticket, ())
order = self.open_orders.get(ticket)
return (order,) if order else ()
elif symbol:
return tuple(order for order in self.orders.get(symbol, ()) if order.ticket in self.live_orders)
return tuple(order for order in self.orders.get(symbol, ()) if order.ticket in self.open_orders)
elif group:
return tuple(self.live_orders.values())
return tuple(order for order in self.open_orders.values())
else:
return tuple(self.live_orders.values())
return tuple(order for order in self.open_orders.values())
def get_positions_total(self):
return len(self.open_positions)
def get_positions(self, symbol: str = '', group: str = '', ticket: int = None) -> tuple[TradePosition, ...]:
if ticket:
position = self.open_positions.get(ticket)
return (position,) if position else ()
elif symbol:
return tuple(position for position in self.positions.get(symbol, ()) if position.ticket in self.open_positions)
elif group:
return tuple(position for position in self.open_positions.values())
else:
return tuple(position for position in self.open_positions.values())
def history_orders_total(self, date_from: datetime | float, date_to: datetime | float):
start =
def history_orders_total(self, date_from: datetime | float, date_to: datetime | float) -> int:
start = int(date_from.timestamp()) if isinstance(date_from, datetime) else int(date_from)
end = int(date_to.timestamp()) if isinstance(date_to, datetime) else int(date_to)
start = self.history_orders[self.history_orders.index >= start].iloc[0].name
end = self.history_orders[self.history_orders.index <= end].iloc[-1].name
return self.history_orders.loc[start:end].shape[0]
def history_orders_get(self, date_from: datetime | float, date_to: datetime | float, group: str = '',
ticket: int = None, position: int = None) -> tuple[TradeOrder, ...]:
start = int(date_from.timestamp()) if isinstance(date_from, datetime) else int(date_from)
end = int(date_to.timestamp()) if isinstance(date_to, datetime) else int(date_to)
start = self.history_orders[self.history_orders.index >= start].iloc[0].name
end = self.history_orders[self.history_orders.index <= end].iloc[-1].name
orders = self.history_orders.loc[start:end]
if ticket:
orders = orders[orders.ticket == ticket]
elif position:
orders = orders[orders.position == position]
elif group:
...
orders.drop(columns=['symbol'], inplace=True)
return tuple(TradeOrder(**order) for order in orders.to_dict(orient='records'))
def get_history_deals_total(self, date_from: datetime | float, date_to: datetime | float) -> int:
start = int(date_from.timestamp()) if isinstance(date_from, datetime) else int(date_from)
end = int(date_to.timestamp()) if isinstance(date_to, datetime) else int(date_to)
start = self.history_deals[self.history_deals.index >= start].iloc[0].name
end = self.history_deals[self.history_deals.index <= end].iloc[-1].name
return self.history_deals.loc[start:end].shape[0]
def get_history_deals(self, date_from: datetime | float, date_to: datetime | float, group: str = '',
position: int = None, ticket: int = None) -> tuple[TradeDeal, ...]:
start = int(date_from.timestamp()) if isinstance(date_from, datetime) else int(date_from)
end = int(date_to.timestamp()) if isinstance(date_to, datetime) else int(date_to)
start = self.history_deals[self.history_deals.index >= start].iloc[0].name
end = self.history_deals[self.history_deals.index <= end].iloc[-1].name
deals = self.history_deals.loc[start:end]
if ticket:
deals = deals[deals.ticket == ticket]
elif position:
deals = deals[deals.position == position]
elif group:
...
deals.drop(columns=['symbol'], inplace=True)
return tuple(TradeDeal(**deal) for deal in deals.to_dict(orient='records'))
+2 -2
View File
@@ -334,9 +334,9 @@ class SymbolInfo(Base):
path: str
def __init__(self, **kwargs):
if name := kwargs.pop('name', None) is None:
if (name := kwargs.pop('name', None)) is None:
raise AttributeError('Symbol Object Must be initialized with a name')
self.name = name
self.name = name # type: str
super().__init__(**kwargs)
def __repr__(self):
+1 -6
View File
@@ -48,7 +48,7 @@ class Symbol(SymbolInfo):
"""
return self.point * 10
async def info_tick(self, *, name: str = "", retries=3) -> Tick:
async def info_tick(self, *, name: str = "") -> Tick:
"""Get the current price tick of a financial instrument.
Args:
@@ -60,16 +60,11 @@ class Symbol(SymbolInfo):
Raises:
ValueError: If request was unsuccessful and None was returned
"""
if retries < 1:
raise ValueError(f'Could not get tick for {name or self.name}. {self.mt5.error}')
tick = await self.mt5.symbol_info_tick(name or self.name)
if tick is not None:
tick = Tick(**tick._asdict())
setattr(self, 'tick', tick) if not name else ...
return tick
if self.mt5.error.is_connection_error():
await asyncio.sleep(retries)
return await self.info_tick(name=name, retries=retries - 1)
raise ValueError(f'Could not get tick for {name or self.name}. {self.mt5.error}')
async def symbol_select(self, *, enable: bool = True) -> bool: