Operar con Python - página 4

 
Malik Arykov #:

Lo que quiero decir es que la integración de python con MT5 no es completa. No se pueden obtener los valores de los indicadores DIRECTAMENTE

Guardar datos en mql
 int i = iBarShift(_Symbol,PERIOD_CURRENT,startTime);
 string nm = _Symbol+"_Inputs.csv";
 int h = FileOpen(nm,FILE_CSV|FILE_WRITE|FILE_COMMON,";");
 double d[2];
 while (i > 0) {
  d[0]=iСustom(_Symbol,PERIOD_CURRENT,"Ind_1",0,i);
  d[1]=iСustom(_Symbol,PERIOD_CURRENT,"Ind_2",0,i);
  FileWrite(h,d[0],d[1]);
  i--;
 }
 FileClose(h);

Lectura de datos en python

import pandas as pd

_Symbol = "EURUSD"
_Base = "C:\\Users\\serg\\AppData\\Roaming\\MetaQuotes\\Terminal\\Common\\Files\\"

url = _Base+_Symbol+"_Inputs.csv"
X=pd.read_csv(url,delimiter=';',header=None)
 
Mikhael1983 #:

Los objetivos del tema son tan sencillos como cinco centavos: permitir que todo el mundo se inicie en el trading algorítmico en Python, simplemente copiando trozos de código como inicializar la comunicación con el terminal, solicitar cotizaciones, pedir al servidor que abra o cierre una operación, etc., etc. Concéntrese en la lógica solamente, y en un lenguaje extremadamente fácil de usar.

Bien... La lógica es exactamente lo que le falta al trading algorítmico: la lógica que se necesita para probar una estrategia de trading antes de que "todos los que vengan" inserten piezas de código y entren en el trading real


de todos modos, buena suerte con tus objetivos "no míticos"

)))

 

Las personas que no necesitan que los precios se guarden en archivos reescribirán un poco el código, y no "escribir en archivos - leer de archivos", sino sólo la estructura de datos que desean. Es en gran medida un escaparate de cosas primitivas, para los que están dispuestos a dar los primeros pasos.

Sin embargo, quienes ya estén familiarizados con conceptos como listas, tuplas, diccionarios, etc.

Propongo leer de los archivos de precios con la función read_all_files_with_prices, que activará la función read_file_with_prices para leer el archivo de precios de cada instrumento.

La función read_file_with_prices devuelve un array numpy.ndarray con filas y columnas correspondientes al fichero de precios original (si los precios se leyeron con éxito y se comprobó la integridad de los datos, cada tipo de elemento es numpy.float64), o la cadena 'no data' si no se leyeron datos o están incompletos .

Que la función read_all_files_with_prices devuelva un diccionario con las cotizaciones de todos los instrumentos. Supongamos que las claves de este diccionario son los nombres de los instrumentos, y los valores son los diccionarios con comillas de un instrumento correspondiente. En los diccionarios con comillas para un símbolo las claves son fecha_hora, los valores son barras (clase bar); además, sugiero proporcionar claves :

  • 'is_data': valor de la clave - Verdadero si hay una cita, Falso - si la lectura del archivo devolvió 'no data',

  • 'instrumento': valor clave - instrumento (str);

  • 'prices': key-value - array de precios del instrumento (numpy.ndarray)

En resumen, esta es la estructura de datos que sugiero:


 

El código de Python está mejor colocado como capturas de pantalla del IDE/editor/sitio.

La falta de resaltado local, al menos de los comentarios de código, hace que sea poco legible, se confunde con el texto principal.

 

Se han añadido funciones para leer archivos y crear la estructura de datos anterior:

import os, time
import datetime as dt
from json import loads, dump
from random import randint  
import numpy as np
from Classes import date_time, Bar 
import Functions 
import MetaTrader5 as mt5


N = 1000 # количество отсчётов, сохраняемых в файлах котировок
N_sd_sell_max = 3 # максимальное количество сделок типа sell по одному инструменту
N_sd_buy_max = 3 # максимальное количество сделок типа buy по одному инструменту
volume = 0.01 # объём сделок 
account_demo = ['Alpari-MT5-Demo', 12345678, 'password']
work_account = account_demo 
work_catalog = 'Z:\\fx_for_forum\\fx\\'
instruments = ['EURUSD_i', 'GBPUSD_i', 'EURGBP_i', 'USDCHF_i', 'USDCAD_i', 'USDJPY_i']



def terminal_init(path_to_terminal, account):
    '''
    Функция осуществляет соединение с терминалом MT5
    '''
    if mt5.initialize(path_to_terminal, server=account[0], login=account[1], password=account[2]):
        str1 = ' - соединение с терминалом {} билд {} установлено'
        print(date_time.now().nice_view, str1.format(mt5.terminal_info().name, mt5.version()[1]))
        return True
    else:
        print(date_time.now().nice_view, ' - соединение с терминалом установить не удалось')
        return False


def terminal_done():
    '''
    Функция завершает соединение с терминалом MT5
    '''    
    try:
        mt5.shutdown()
        print('\n' + date_time.now().nice_view, ' - соединение с терминалом успешно завершено')
    except:
        print('\n' + date_time.now().nice_view, ' - соединение с терминалом завершить не удалось')


def dt_stamp_from_M5_view(M5_view):
    return date_time(int(M5_view[0:4]), int(M5_view[4:6]), int(M5_view[6:8]), int(M5_view[8:10]), int(M5_view[10:12])) 


def pips_definer(instrument):
    '''
    Функция возвращает величину пипса для инструмента, например для EURUSD - 0.0001, для USDJPY - 0.01
    '''
    if instrument in ['EURUSD_i', 'GBPUSD_i', 'EURGBP_i', 'USDCHF_i', 'USDCAD_i']:
        return 0.0001
    elif instrument in ['USDJPY_i']:
        return 0.01
    else:
        return None


def save_prices_to_file(instrument, n=100):
    '''
    Функция принимает инструмент, количество отсчётов цены n в таймфрейме М5,
    и сохраняет в файл (только сформировавшиеся полностью бары, время соответствует времени закрытия (!) бара)
    '''
    w = pips_definer(instrument)
    tochn = int(abs(np.log10(w))+1) 
    path_file = os.path.join(work_catalog, instrument+'.txt')
   
    bars = mt5.copy_rates_from_pos(instrument, mt5.TIMEFRAME_M5, 0, n+1) # в случае ошибки mt5.copy_rates_from_pos возвращает None
   
    try:
        f = open(path_file, 'w')
        for i in range(0, n+1):
            bar = bars[i]
            dt_stamp = date_time.fromtimestamp(bar[0], dt.timezone.utc) + dt.timedelta(hours=1, minutes=5)
            open_ = str(round(bar[1], tochn))
            high_ = str(round(bar[2], tochn))
            low_ = str(round(bar[3], tochn))
            close_ = str(round(bar[4], tochn))
            if i != n:
                f.write(dt_stamp.M5_view + ' ' + open_ + ' ' + high_ + ' ' + low_ + ' ' + close_ + '\n')
        f.close()   
        print(date_time.now().nice_view, ' - котировки {} успешно записаны в файл'.format(instrument))
    except:
        print(date_time.now().nice_view, ' - котировки {} записать в файл не удалось'.format(instrument))


def read_file_with_prices(catalog, instrument):
    '''
    Функция принимает каталог, и наименование инструмента, читает в указанном каталоге файл с ценами для указанного инструмента
    (формат данных: отсчёт времени в виде строки формата M5_view, open, high, low, close), проверяет цены на целостность данных.
    Функция возвращает или массив ndarray (если всё успешно), или строку 'no data' (если данные не прочитались, или неполны)  
    '''   
    path_file = os.path.join(catalog, instrument+'.txt')
    try:
        f = open(path_file, 'r')
        ndarray___with_prices_for_instrument = np.loadtxt(f, delimiter=' ', dtype='float64')
        f.close()
    except:
        print(date_time.now().nice_view + ' - Файл с котировками для {} прочитать не удалось'.format(instrument))
        return 'no data'
    # проверка данных на целостность
    timedelta_5 = dt.timedelta(minutes=5)
    nx = ndarray___with_prices_for_instrument.shape[0]
    for i in range(1, nx):
        dt_stamp_i = dt_stamp_from_M5_view(str(int(ndarray___with_prices_for_instrument[i, 0])))
        dt_stamp_im1 = dt_stamp_from_M5_view(str(int(ndarray___with_prices_for_instrument[i-1, 0]))) 
        t_delta = dt_stamp_i - dt_stamp_im1
        if t_delta != timedelta_5:
            if t_delta >= dt.timedelta(hours=47) and t_delta <= dt.timedelta(hours=50):
                pass # разрыв данных с вечера пятницы до понедельника
            else:
                t1 = ' - Котировки для {} неполны, и не будут переданы на выполнение расчётов\nИменно - ошибка при {}'
                print((date_time.now().nice_view + t1).format(instrument, dt_stamp_im1.M5_view))
                return 'no data'
    return ndarray___with_prices_for_instrument


def read_all_files_with_prices(catalog, instruments):
    '''
    Функция принимает каталог, и список инструментов, читает в указанном каталоге файлы с ценами для указанных инструментов
    с использованием функции read_file_with_prices.
    Функция возвращает словарь prices_for_all_instruments с котировками по всем инструментам.
    В этом словаре ключи - наименования инструментов, значения - словари с котировками по одному соответствующему инструменту.
    В словарях с котировками по одному инструменту ключи - отсчёты даты-времени (класс date_time), значения - бары (класс bar),
    кроме того предусмотрены ключи:
        - 'is_data' - значение по ключу - True, если котировки в наличии, False - если функция чтения из файла вернула 'no data',
        - 'instrument' - значение по ключу - инструмент (str); 
        - 'prices' - значение по ключу - массив котировок по инструменту (numpy.ndarray)
    '''
    dict___with_prices_for_all_instruments = {}
    for instrument in instruments:
        w = pips_definer(instrument)
        ndarray___with_prices_for_instrument = read_file_with_prices(catalog, instrument)
        dict___with_prices_for_instrument = {}
        if type(ndarray___with_prices_for_instrument) == type('no data'):
            dict___with_prices_for_instrument['is_data'] = False
        else:
            dict___with_prices_for_instrument['is_data'] = True
            dict___with_prices_for_instrument['instrument'] = instrument
            dict___with_prices_for_instrument['prices'] = ndarray___with_prices_for_instrument
            for i in range(0, ndarray___with_prices_for_instrument.shape[0]):
                dt_stamp = dt_stamp_from_M5_view(str(int(ndarray___with_prices_for_instrument[i, 0])))
                dict___with_prices_for_instrument[dt_stamp] = Bar(instrument, 5, dt_stamp,
                                                                  ndarray___with_prices_for_instrument[i, 1],
                                                                  ndarray___with_prices_for_instrument[i, 3],
                                                                  ndarray___with_prices_for_instrument[i, 2],
                                                                  ndarray___with_prices_for_instrument[i, 4], w)
        dict___with_prices_for_all_instruments[instrument] = dict___with_prices_for_instrument
    return dict___with_prices_for_all_instruments





def main(N):
   
    '''
    Главная функция, обеспечивающая в бесконечном цикле связь с терминалом,
    сохранение котировок, и запуск функции, осуществляющей торговлю
    '''   
   
    dt_start = date_time.now()
    dt_write = dt_stamp_from_M5_view(dt_start.M5_view) + dt.timedelta(minutes=5, seconds=10)
    print('\n' + dt_start.nice_view, ' - начал работу, бездействую до ' + dt_write.nice_view + '\n')
    timedelta_sleep = dt_write - date_time.now()
    time.sleep(timedelta_sleep.seconds)
   
    while True:
       
        # установка соединения с MetaTrader5
        if terminal_init(os.path.join('C:\\', 'Program Files', 'Alpari MT5', 'terminal64.exe'), work_account):
           
            # запись цен в файлы: для всех инструментов, N отсчётов 
            if (dt.datetime.now() - dt_write) < dt.timedelta(seconds=10):
                print('\n' + date_time.now().nice_view + '  - начинаю запись цен в файлы для всех инструментов')
                for instrument in instruments:
                    save_prices_to_file(instrument, N)
           
            # пауза 5 секунд после записи файлов с ценами, возможно необходимых для открытия в Mathcad,
            # или ещё зачем, если в них нет нужды, желающие могут переписать код, исключив бессмысленную
            # последовательность записи файлов, и затем чтения файлов, сразу сформировав нужную им структуру данных
            time.sleep(5)
           
            # определяем значение последнего отсчёта времени, который должен быть в файлах котировок, если они актуальны 
            # (в выходные дни это будет не так, а в будни, пока рынок открыт - так)
            fx_dt_stamp_now = dt_stamp_from_M5_view(date_time.now().M5_view)   
            print('\n\n'+date_time.now().nice_view, '- начинаю вычисления для отсчёта времени {}'.format(fx_dt_stamp_now.M5_view))
           
            # получаем словарь с ценами для всех инструментов 
            dict___with_prices_for_all_instruments = read_all_files_with_prices(work_catalog, instruments)
           
            # демонстрация актуальности котировок,
            # поскольку сейчас выходной, то последний отсчёт времени в файлах данных не совпадает с текущим, не суть  
            for instrument in instruments:
                if dict___with_prices_for_all_instruments[instrument]['is_data']:
                    str1 = 'Последний отсчёт в файле с ценами для {}: {}'
                    print(str1.format(instrument, str(int(dict___with_prices_for_all_instruments[instrument]['prices'][N-1, 0]))))
           
           
           
            # завершение соединения с MetaTrader5
            terminal_done()
       
            # определение параметров ожидания до следующего выполнения тела цикла
            dt_start = date_time.now() 
            dt_write = dt_stamp_from_M5_view(dt_start.M5_view) + dt.timedelta(minutes=5, seconds=10)
            timedelta_sleep = dt_write - dt_start
            print(date_time.now().nice_view + '  - засыпаю до {}\n\n\n\n\n'.format(dt_write.nice_view)) 
            time.sleep(timedelta_sleep.seconds)
       


if __name__ == '__main__':
    main(N)
 
Maxim Kuznetsov #:

El código de Python está mejor colocado como capturas de pantalla del IDE/editor/sitio.

La falta de resaltado local, al menos para los comentarios de código, hace que sea poco legible, se mezcla con el texto principal.

Pero se puede copiar y abrir en el IDE, pero desde la imagen se puede volver a escribir manualmente excepto...


El trabajo del programa mostrado arriba:



Básicamente, casi todo está listo para empezar a analizar datos y tomar y ejecutar decisiones de trading.

 
Mikhael1983 #:

Pero se puede copiar y abrir en el IDE, mientras que la imagen sólo se puede reescribir a mano...


No creo que nadie se moleste en copiar y pegar el código... nadie lo necesita aquí

para leer y discutir, sí. Recoge algo curioso o da alguna pista. Y no creo que lo cargue en el IDE. La gente publica en el foro para que otros lo lean y para usar el código lo adjuntan o lo enlazan al repositorio (o los combinan).

Pero esto es una distracción.

 

Ponga el siguiente código en el archivo Functions.py, una función que calcula la SMA

import numpy as np 


def SMA(B, s, k, n, d): 
    A = B[B.size-n-s-d-k:B.size-d-k]
    Sigma = np.sum(A[n:n+s]) 
    C = np.zeros(n)
    C[n-1] = Sigma   
    for q in range(1, n): 
        Sigma = Sigma - A[n-q+s] + A[n-q]
        C[n-1-q] = Sigma  
    return C/s 

Necesito los parámetros d y k para realizar un desplazamiento temporal, no el punto, los pondremos a cero.

 

Se me ocurrirá una simple lógica tonta, como:

para cada instrumento calculamos la SMA con un retraso de z=100 intervalos entre muestras, es decir, la SMA del orden s=1+2*z=201.

Si no hay operaciones en el mercado o su cantidad (para un determinado símbolo) es inferior a la cantidad admisible (por separado para Vender y Comprar)

si el precio está actualmente 30 pips o más por encima de la SMA(201) - abrir operación de venta con SL=TP=50 pips,

si el precio está actualmente 30 pips o más por debajo de la SMA(201) - abra una operación de compra con SL=TP=50 pips.

Si en algún momento el beneficio de la operación alcanza los 20 pips - ciérrela sin esperar al cierre por SL o TP.

Pongamos como ejemplo una operación de este tipo. Simple, tonto, desde el punto de vista comercial, pero la esencia del asunto es la aplicación.

 

Sugiero estas funciones para la apertura y el cierre de las operaciones:

def open_trade(buy_or_sell, instr, volume, price_in, price_sl, price_tp, w, deviation_in_pips): 
    '''
    Функция осуществляет проверку: не ушёл ли текущий уровень цены от ожидаемого уровня price_in, 
    и если всё хорошо, совершает открытие сделки
    ''' 
    
    dopustimaya_delta_ot_price_in_in_pips = 5
        
    if buy_or_sell == 'buy': 
        trade_type = mt5.ORDER_TYPE_BUY 
        price = mt5.symbol_info_tick(instr).ask 
        comment = 'buy via Python' 
    elif buy_or_sell == 'sell': 
        trade_type = mt5.ORDER_TYPE_SELL 
        price = mt5.symbol_info_tick(instr).bid
        comment = 'sell via Python'
    
    if abs((price - price_in)/w) < dopustimaya_delta_ot_price_in_in_pips: 
        
        trade_request = {'action': mt5.TRADE_ACTION_DEAL, 
                         'symbol': instr, 
                         'volume': volume, 
                         'type': trade_type, 
                         'price': price, 
                         'sl': price_sl, 
                         'tp': price_tp, 
                         'deviation': deviation_in_pips*10, 
                         'magic':  12345, 
                         'comment': comment, 
                         'type_time': mt5.ORDER_TIME_GTC, 
                         'type_filling': mt5.ORDER_FILLING_FOK}
        
        # отправление торгового запроса на сервер 
        result = mt5.order_send(trade_request) 
        # print(result)
        
        if result.comment == 'Request executed': 
            if buy_or_sell == 'buy': 
                print(date_time.now().nice_view, ' - сделка на покупку {} открыта'.format(instr)) 
            elif buy_or_sell == 'sell': 
                print(date_time.now().nice_view, ' - сделка на продажу {} открыта'.format(instr)) 
        else: 
            if buy_or_sell == 'buy': 
                print(date_time.now().nice_view, ' - сделку на покупку {} открыть не удалось'.format(instr)) 
            elif buy_or_sell == 'sell': 
                print(date_time.now().nice_view, ' - сделку на продажу {} открыть не удалось'.format(instr)) 
        return result
    else: 
        print(date_time.now().nice_view, ' - цена ушла от ожидаемой >5 пипс, сделку по {} не открываю'.format(instr))
        return None 


def close_trade(position): 
    '''
    Функция осуществляет закрытие сделки
    ''' 
    
    instr = position.symbol 
    
    if position.tp > position.sl: # buy 
        trade_type = mt5.ORDER_TYPE_SELL # если позиция buy, то для её закрытия нужно sell  
        price = mt5.symbol_info_tick(instr).bid 
    elif position.sl > position.tp: # sell 
        trade_type = mt5.ORDER_TYPE_BUY # если позиция sell, то для её закрытия нужно buy 
        price = mt5.symbol_info_tick(instr).ask 
    
    position_id = position.ticket 
    volume = position.volume 
    
    trade_request = {'action': mt5.TRADE_ACTION_DEAL, 
                     'symbol': instr, 
                     'volume': volume, 
                     'type': trade_type, 
                     'position': position_id, 
                     'price': price, 
                     #'deviation': deviation_in_pips*10, 
                     'magic':  position.magic, 
                     'comment': 'close via python', 
                     'type_time': mt5.ORDER_TIME_GTC, 
                     'type_filling': mt5.ORDER_FILLING_FOK}
    
    # отправление торгового запроса на сервер 
    result = mt5.order_send(trade_request) 
    
    if result.comment == 'Request executed': 
        print(date_time.now().nice_view, ' - сделка {} по {} закрыта'.format(position_id, instr)) 
    
    return result