Trading con Python - pagina 4

 
Malik Arykov #:

Il mio punto è che l'integrazione di python con MT5 non è completa. Non è possibile ottenere i valori dell'indicatore DIRETTAMENTE

Salvare i dati in 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);

Leggere i dati in 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 #:

Gli obiettivi dell'argomento sono semplici come cinque centesimi: permettere a chiunque di iniziare il trading algoritmico in Python, semplicemente copiando pezzi di codice come inizializzare la comunicazione con il terminale, richiedere quotazioni, chiedere al server di aprire o chiudere un trade, ecc. Si concentra solo sulla logica, e in un linguaggio estremamente facile da usare.

Giusto... La logica è esattamente ciò che manca al trading algoritmico - la logica di cui avete bisogno per testare una strategia di trading prima che "all comers" inserisca pezzi di codice ed entri nel trading reale


buona fortuna con i tuoi obiettivi "non mitici" comunque

)))

 

Le persone che non hanno bisogno di prezzi salvati su file riscriveranno un po' il codice, e non "scrivere su file - leggere da file", ma solo la struttura dei dati che vogliono. È in gran parte solo una vetrina di cose primitive, per chi è disposto a fare i primi passi.

Tuttavia, coloro che hanno già familiarità con concetti come liste, tuple, dizionari, ecc.

Propongo di leggere dai file dei prezzi con la funzione read_all_files_with_prices, che attiverà la funzione read_file_with_prices per leggere il file dei prezzi di ogni strumento.

Lascia che la funzione read_file_with_prices restituisca un array numpy.ndarray con righe e colonne corrispondenti al file originale dei prezzi (se i prezzi sono stati letti con successo e controllati per l'integrità dei dati, ogni tipo di elemento è numpy.float64), o la stringa 'no data' se nessun dato è stato letto o incompleto .

Che la funzione read_all_files_with_prices restituisca un dizionario con le quotazioni di tutti gli strumenti. Supponiamo che le chiavi di questo dizionario siano i nomi degli strumenti e i valori siano i dizionari con le virgolette di uno strumento corrispondente. Nei dizionari con le virgolette per un simbolo le chiavi sono date_time, i valori sono barre (classe bar); inoltre, suggerisco di fornire chiavi :

  • 'is_data': valore chiave - True se c'è una citazione, False - se la lettura dal file restituisce 'no data',

  • 'instrument': valore chiave - instrument (str);

  • 'prices': key-value - array di prezzi per lo strumento (numpy.ndarray)

In breve, questa è la struttura dei dati che suggerisco:


 

Il codice Python è meglio messo come screenshot dall'IDE/editor/sito.

La mancanza di evidenziazione locale, almeno dei commenti al codice, lo rende poco leggibile, si confonde con il testo principale.

 

Aggiunte funzioni per leggere i file e creare la suddetta struttura di dati:

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 #:

Il codice Python è meglio messo come screenshot dall'IDE/editor/sito.

La mancanza di evidenziazione locale, almeno per i commenti al codice, lo rende poco leggibile, si confonde con il testo principale.

Ma può essere copiato e aperto nell'IDE, ma dall'immagine può essere riscritto manualmente tranne...


Il lavoro del programma mostrato sopra:



Fondamentalmente, quasi tutto è pronto per iniziare ad analizzare i dati e prendere ed eseguire decisioni di trading.

 
Mikhael1983 #:

Ma può essere copiato e aperto nell'IDE, mentre l'immagine può essere riscritta solo a mano...


Non credo che qualcuno si prenderà la briga di copiare e incollare il codice... nessuno ne ha bisogno qui

per leggere e discutere, sì. Raccogliete qualcosa di curioso o date qualche suggerimento. E non credo che lo porterò in giro in IDE. Le persone pubblicano sul forum per la lettura da parte di altri e per l'utilizzo del codice o lo allegano o il link al repository (o li combinano).

Ma questo mi distrae.

 

Metti il seguente codice nel file Functions.py, una funzione che calcola 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 

Ho bisogno dei parametri d e k per eseguire uno spostamento temporale, non il punto, li metteremo a zero.

 

Mi viene in mente una semplice logica stupida, tipo:

per ogni strumento calcoliamo la SMA in ritardo di z=100 intervalli tra i campioni, cioè la SMA dell'ordine s=1+2*z=201.

Se non ci sono compravendite nel mercato o la loro quantità (per un certo simbolo) è inferiore alla quantità ammissibile (separatamente per Vendere e Comprare)

se il prezzo è attualmente 30 pip o più sopra la SMA(201) - aprire un'operazione di vendita con SL=TP=50 pip,

se il prezzo è attualmente 30 pip o più sotto la SMA(201) - aprire un trade Buy con SL=TP=50 pip.

Se in qualsiasi momento il profitto sul trade raggiunge i 20 pip - chiudilo senza aspettare la chiusura di SL o TP.

Implementiamo un tale commercio come esempio. Semplice, sciocco, dal punto di vista del trading, ma l'essenza della questione è l'implementazione.

 

Suggerisco tali funzioni per l'apertura e la chiusura delle transazioni:

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
Motivazione: