Pythonでトレーディング - ページ 6

 
Алексей КоКоКо #:
私はトピックスターターをサポートします、私は4と5として何度もmqlを借りて、私は個人的に私は取引でここにのみ使用できる言語を学ぶために少し欲求を持っていると言うでしょう...

まあ、それは何もしていないか、問題を理解していないかのどちらかでしょう。本当にインジケーターのアルゴリズムをもう一度書き直すんですか?ボルゴグラードからウラジオストクを経由してモスクワに行くようなものです。もうひとつ、すべての指標は履歴を考慮している。つまり、1ヶ月分のデータ(バー)を取り、アルゴリズムに従ってインディケータを計算し、Terminalと比較すると、計算結果が違ってくるのです。

 
Vitaly Muzichenko #:

私もこのフレーズは理解できません。

書いた内容から、Pythonの知識層が厚いことが判明 :)エディタを開けばpythonをすでに知っていて、mqlを開けば何も知らないというのはとても簡単です。

同時に、完全にプラットフォームオリエンテッドな mqlを「時代遅れ」の道具と呼ぶのは.pythonは1991年に作られたので、これはもっと前です。

このスレッドで見た、pythonで書かれたものは、mqlで実装するのは非常に簡単です。

---

まあ、一般論としては面白いのですが、それ以上ではないですね。

しかも、Pythonの利点を生かしきれていないし、「Python風」でもない。ドライバーで釘を打つようなもので、ハンマーは旧式なんです。

イゴール・マカヌ#:

あなたの家の食器はスプーンだけなのではないでしょうか。スープやケーキを作るのに、その方が安全です

)))

もし好きなら、Pythonを使ってください。ただし、トピックスターターとしてではなく、独自のカスタムデータ型(バーなど)を作らず、独自の計算を書かず、既存のソリューションを使う ようにしてください。

はい) ところで、Pythonにはそういったトレーダー向け関数のライブラリはありませんし、数行で系列の完全な統計解析が書けるのであればMAは必要ないのですが......。)

 
Aleksey Mavrin #:

ええ)ところで、トレーディングに近い各種関数のPythonライブラリがないとか、数行で系列の完全な統計分析が書けるなら、MAはもう必要ないとか)......。

Pythonアルゴリズムトレーディングライブラリベスト101|PythonRepo

 

昨日フォーラムに戻ることができませんでした。続けます。新しいデモアカウントを開始しましたが、最後に「_i」が付いていない楽器の名前があるので、この部分のコードを修正しました。

便利な方のために、マイクロチュートリアルの一種である緩やかでゆっくりとした動きを続けていきます。

変数n(小さい)を導入しましょう-いわばウィンドウの長さです。つまり、価格、たとえば1000、nのNの読み取り値を持つファイルでは、たとえば10、100、または288(時間枠がM5の場合は1日)を設定しましょう。

私にとっての変数dは、このウィンドウが時間内に過去にシフトしたことを意味します。 d=0に設定しましょう。


たとえば、終値の10の読み取り値と101のオーダーのSMA(読み取り値の間に50の間隔で遅延)を表示します。


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 = 10 
d = 0
N_sd_sell_max = 3 # максимальное количество сделок типа sell по одному инструменту 
N_sd_buy_max = 3 # максимальное количество сделок типа buy по одному инструменту 
volume = 0.01 # объём сделок  
account_demo = ['Alpari-MT5-Demo', 51056680 , 'password'] 
work_account = account_demo  
work_catalog = 'Z:\\fx_for_forum\\fx\\'
instruments = ['EURUSD', 'GBPUSD', 'EURGBP', 'USDCHF', 'USDCAD', 'USDJPY'] 



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', 'GBPUSD', 'EURGBP', 'USDCHF', 'USDCAD']: 
         return 0.0001 
    elif instrument in ['USDJPY']: 
         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 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






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 ]))))
            
            # отобразим значения последних n отсчётов цен закрытия баров, и SMA101, вычисленной по ценам закрытия баров  
             for instrument in instruments: 
                dict___with_prices_for_instrument = dict___with_prices_for_all_instruments[instrument] 
                 if dict___with_prices_for_instrument['is_data']: 
                    AData = dict___with_prices_for_instrument['prices'] 
                    Ax_DTID  = AData[:, 0 ] # столбец отсчётов даты-времени в виде M5_view в N строк, пока низачем не нужен 
                    Ax_close = AData[:, 4 ] # столбец отсчётов цен закрытия в N строк 
                    A_DTID  = Ax_DTID[Ax_close.size-n-d:Ax_close.size-d] # столбец отсчётов даты-времени в виде M5_view в n строк  
                    A_close = Ax_close[Ax_close.size-n-d:Ax_close.size-d] # столбец отсчётов цен закрытия в n строк 
                    print('Последние {} отсчётов цен закрытия для {}:'.format(n, instrument)) 
                    print(A_close) 
                    z = 50 # параметр, задающий запаздывание скользящей средней, для z= 50 скользящая средняя по 1 + 2 * 50 = 101 отсчёту  
                    A_close_s = Functions.SMA(Ax_close, 1 + 2 *z, 0 , n, d) 
                    print('Последние {} отсчётов SMA( 101 ) по ценам закрытия для {}:'.format(n, instrument)) 
                    print(A_close_s) 
                
            # завершение соединения с 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)


作業の結果:


 

いわば、市場の現在の取引について、端末に問い合わせる方法を学びましょう。

関数を紹介します。

def buy_or_sell_opredelitel(position): 
    try: 
        tp = position.tp 
        sl = position.sl 
        if tp > sl: 
            return 'buy' 
        elif sl > tp: 
            return 'sell' 
        else: 
            return None        
    except: 
        return None 


def Nsell_Nbuy_calculator(current_positions): 
    '''
    Функция принимает перечень открытых позиций. 
    Функция возвращает кортеж (Nsell, Nbuy) с количествами позиций sell и buy, соответственно 
    ''' 
    Nsell = 0 
    Nbuy = 0 
    if len(current_positions) == 0: 
        return (Nsell, Nbuy) 
    else: 
        for position in current_positions: 
            if buy_or_sell_opredelitel(position) == 'sell': 
                Nsell += 1 
            elif buy_or_sell_opredelitel(position) == 'buy': 
                Nbuy += 1 
        return (Nsell, Nbuy)

シンボルのオープンポジションのリストを取り、タプル 売るべき取引の数、買うべき取引の数)を返すようにします。

main 関数の中で、関数 terminal_done() の前に次のコードを追加します。

            # запрос открытых сделок по инструменту 
            for instrument in instruments: 
                current_positions_for_instrument = mt5.positions_get(symbol = instrument) 
                Nsell_Nbuy = Nsell_Nbuy_calculator(current_positions_for_instrument) 
                print(date_time.now().nice_view, ' - по {} открытых сделок sell {}, открытых сделок buy {}'.format(instrument, 
                                                                                                                   Nsell_Nbuy[0], 
                                                                                                                   Nsell_Nbuy[1]))

結果(出力の都合上、nを5にしています)。


実際の取引を開始することを提案します。例えば、SMAよりある程度高ければ売り、SMAよりある程度低ければ買いの取引をします。

オープントレードをSLとTPで制御し、無ければクローズする機能、現在の結果(利益または損失)をpipsで返す機能など、あと2、3機能を提案します。

def SL_TP_control(instrument, current_positions): 
    '''
    Функция принимает перечень открытых позиций. 
    Функция осуществляет контроль выставленности SL и TP у открытых позиций, 
    в случае обнаружения сделки без SL или TP - закрывает (пытается закрыть) все сделки из полученного перечня.   
    ''' 
    Nsell_Nbuy = Nsell_Nbuy_calculator(current_positions)  
    if Nsell_Nbuy[0] > 0 or Nsell_Nbuy[1] > 0:   
        print(date_time.now().nice_view, ' - осуществляю проверку выставленности TP и SL у открытых сделок по {}'.format(instrument)) 
        print(date_time.now().nice_view, ' - открытых сделок sell {}, открытых сделок buy {}'.format(Nsell_Nbuy[0], Nsell_Nbuy[1])) 
        s = 0 
        for i in range(len(current_positions)): 
            try: 
                if current_positions[i].tp > current_positions[i].sl or current_positions[i].sl > current_positions[i].tp: 
                    s += 1 
            except: 
                print(date_time.now().nice_view, ' - обнаружена сделка по {} с отсутствующим SL или TP - закрываю!'.format(instrument)) 
                close_trade(current_positions[i]) 
                time.sleep(0.5) 
        if s == len(current_positions): 
            print(date_time.now().nice_view, ' - выставленность TP и SL у открытых сделок по {} - OK'.format(instrument)) 

def profit_calculator(instrument, position): 
    '''
    Функция принимает позицию, и возвращает текущий результат, в пипсах. 
    '''
    w = pips_definer(instrument) 
    buy_sell = buy_or_sell_opredelitel(position) 
    if buy_sell == 'sell': 
        bs = -1 
    elif buy_sell == 'buy': 
        bs = 1 
    if buy_sell == 'buy' or buy_sell == 'sell': 
        profit_in_pips = round((position.price_current - position.price_open)/w, 1)*bs  
        return profit_in_pips 
    return None 
 

注文がすでに履歴に残っていて、有効なポジションにない場合、どうにかして処理することを忘れてはならないし、この場合、オープンのための注文を再送信することはない。

このような状況は時々発生し、例えばTrade.mqhを通して作業してもこの問題は解決しません(少なくとも以前はそうでした - 私はずいぶん前にTrade.mqhを諦めました)。

時にはその逆もあります。決済の注文はすでに履歴に残っており、ポジションはまだ表示されています。

 
JRandomTrader #:

注文がすでに履歴に残っていて、有効なポジションにない場合、どうにかして処理することを忘れてはならないし、この場合、オープンのための注文を再送信することはない。

このような状況は時々発生し、例えばTrade.mqhを通して作業してもこの問題は解決しません(少なくとも以前はそうでした - 私はずいぶん前にTrade.mqhを諦めました)。

時にはその逆もあります。決済の注文はすでに履歴に残っており、ポジションはまだ表示されています。


端末にオープンポジションを問い合わせる方法を紹介しました。もし、そのポジションがすでに存在していれば、リクエストを再送する必要はありませんが、そうでない場合は再送します。特に、リクエストを送った後に、そのプロパティがすべて入ったオブジェクトが返ってくることで、それが正常なのか、どんなエラーがあるのかがわかり、それを元にした行為ができることです。ここは問題ありません。

 

これまでのTradeLogic.pyのフルコード(原始的ですが、Metatrader 5とPythonを使ったトレードへの扉を開くことができるかもしれませんね。)

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 = 5 
d = 0 
price_SMA_razn_Level = 10 # модуль разности между ценой и SMA, при превышении этого порога открываем сделку 
N_sd_sell_max = 2 # максимальное количество сделок типа sell по одному инструменту 
N_sd_buy_max = 2 # максимальное количество сделок типа buy по одному инструменту 
SL = 50; TP = 50 # уровни SL и TP, в пипсах 
volume = 0.01 # объём сделок  
account_demo = ['Alpari-MT5-Demo', 51056680, 'pxav2sxs'] 
work_account = account_demo  
work_catalog = 'Z:\\fx_for_forum\\fx\\'
instruments = ['EURUSD', 'GBPUSD', 'EURGBP', 'USDCHF', 'USDCAD', 'USDJPY'] 



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', 'GBPUSD', 'EURGBP', 'USDCHF', 'USDCAD']: 
        return 0.0001 
    elif instrument in ['USDJPY']: 
        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 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


def buy_or_sell_opredelitel(position): 
    try: 
        tp = position.tp 
        sl = position.sl 
        if tp > sl: 
            return 'buy' 
        elif sl > tp: 
            return 'sell' 
        else: 
            return None        
    except: 
        return None 


def Nsell_Nbuy_calculator(current_positions): 
    '''
    Функция принимает перечень открытых позиций. 
    Функция возвращает кортеж (Nsell, Nbuy) с количествами позиций sell и buy, соответственно 
    ''' 
    Nsell = 0 
    Nbuy = 0 
    if len(current_positions) == 0: 
        return (Nsell, Nbuy) 
    else: 
        for position in current_positions: 
            if buy_or_sell_opredelitel(position) == 'sell': 
                Nsell += 1 
            elif buy_or_sell_opredelitel(position) == 'buy': 
                Nbuy += 1 
        return (Nsell, Nbuy)


def SL_TP_control(instrument, current_positions): 
    '''
    Функция принимает перечень открытых позиций. 
    Функция осуществляет контроль выставленности SL и TP у открытых позиций, 
    в случае обнаружения сделки без SL или TP - закрывает (пытается закрыть) все сделки из полученного перечня.   
    ''' 
    Nsell_Nbuy = Nsell_Nbuy_calculator(current_positions)  
    if Nsell_Nbuy[0] > 0 or Nsell_Nbuy[1] > 0:   
        print(date_time.now().nice_view, ' - осуществляю проверку выставленности TP и SL у открытых сделок по {}'.format(instrument)) 
        print(date_time.now().nice_view, ' - открытых сделок sell {}, открытых сделок buy {}'.format(Nsell_Nbuy[0], Nsell_Nbuy[1])) 
        s = 0 
        for i in range(len(current_positions)): 
            try: 
                if current_positions[i].tp > current_positions[i].sl or current_positions[i].sl > current_positions[i].tp: 
                    s += 1 
            except: 
                print(date_time.now().nice_view, ' - обнаружена сделка по {} с отсутствующим SL или TP - закрываю!'.format(instrument)) 
                close_trade(current_positions[i]) 
                time.sleep(0.5) 
        if s == len(current_positions): 
            print(date_time.now().nice_view, ' - выставленность TP и SL у открытых сделок по {} - OK'.format(instrument)) 


def profit_calculator(instrument, position): 
    '''
    Функция принимает позицию, и возвращает текущий результат, в пипсах. 
    '''
    w = pips_definer(instrument) 
    buy_sell = buy_or_sell_opredelitel(position) 
    if buy_sell == 'sell': 
        bs = -1 
    elif buy_sell == 'buy': 
        bs = 1 
    if buy_sell == 'buy' or buy_sell == 'sell': 
        profit_in_pips = round((position.price_current - position.price_open)/w, 1)*bs  
        return profit_in_pips 
    return None 




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) 
                 
            # вычислим значения последних n отсчётов цен закрытия баров, и SMA101, вычисленной по ценам закрытия баров, 
            # если цена выше SMA на price_SMA_razn_Level, в пипсах, и открытых сделок на продажу по этому инструменту меньше, 
            # чем N_sd_sell_max, то открываем сделку на продажу, 
            # если цена ниже SMA на price_SMA_razn_Level, в пипсах, и открытых сделок на покупку по этому инструменту меньше, 
            # чем N_sd_buy_max, то открываем сделку на покупку. 
            for instrument in instruments: 
                dict___with_prices_for_instrument = dict___with_prices_for_all_instruments[instrument] 
                current_positions_for_instrument = mt5.positions_get(symbol = instrument) 
                Nsell_Nbuy = Nsell_Nbuy_calculator(current_positions_for_instrument) 
                if dict___with_prices_for_instrument['is_data']: 
                    w = pips_definer(instrument) # величина пипса для инструмента 
                    tochn = int(abs(np.log10(w))+1) 
                    AData = dict___with_prices_for_instrument['prices'] 
                    Ax_close = AData[:, 4] # столбец отсчётов цен закрытия в N строк 
                    A_close = Ax_close[Ax_close.size-n-d:Ax_close.size-d] # столбец отсчётов цен закрытия в n строк 
                    z = 50 # параметр, задающий запаздывание скользящей средней, для z=50 скользящая средняя по 1+2*50=101 отсчёту  
                    A_close_s = Functions.SMA(Ax_close, 1+2*z, 0, n, d)           
                    price_SMA_razn_for_instrument = round((A_close[n-1] - A_close_s[n-1])/w, 1) 
                    print('\n')
                    str1 = ' - для {} крайние справа отсчёты цены {}, SMA {}, разность {} пипс' 
                    print(date_time.now().nice_view, str1.format(instrument, A_close[n-1], round(A_close_s[n-1], tochn), 
                                                                       price_SMA_razn_for_instrument))
                    if abs(price_SMA_razn_for_instrument) < price_SMA_razn_Level: 
                        print(date_time.now().nice_view, ' - разность с SMA не превышает порога {} пипс, ничего не делаю'.format(price_SMA_razn_Level)) 
                    elif abs(price_SMA_razn_for_instrument) > price_SMA_razn_Level: 
                        if price_SMA_razn_for_instrument > price_SMA_razn_Level and Nsell_Nbuy[0] < N_sd_sell_max: 
                            print(date_time.now().nice_view, ' - разность с SMA превышает порог {} пипс, продаю {}'.format(price_SMA_razn_Level, 
                                                                                                                           instrument)) 
                            open_trade('sell', instrument, volume, A_close[n-1], A_close[n-1]+SL*w, A_close[n-1]-TP*w, w, 2) 
                            time.sleep(1)
                        if price_SMA_razn_for_instrument < -price_SMA_razn_Level and Nsell_Nbuy[1] < N_sd_buy_max: 
                            print(date_time.now().nice_view, ' - разность с SMA превышает порог {} пипс, покупаю {}'.format(price_SMA_razn_Level, 
                                                                                                                            instrument))  
                            open_trade('buy', instrument, volume, A_close[n-1], A_close[n-1]-SL*w, A_close[n-1]+TP*w, w, 2) 
                            time.sleep(1)
                            
            # запрос открытых сделок по инструменту 
            print('\n')
            for instrument in instruments: 
                current_positions_for_instrument = mt5.positions_get(symbol = instrument) 
                Nsell_Nbuy = Nsell_Nbuy_calculator(current_positions_for_instrument) 
                print(date_time.now().nice_view, ' - по {} открытых сделок sell {}, открытых сделок buy {}'.format(instrument, 
                                                                                                                   Nsell_Nbuy[0], 
                                                                                                                   Nsell_Nbuy[1]))
            
            # завершение соединения с 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)


仕事の成果



Z.I.Y.最も原始的で初歩的なコードの断片は、私の意見では、全くどんなロジックも、どんな計算も、どんな比較も、様々な条件によるオープントレードの制御も、ある条件下では自動的にクローズも、コマンドラインとファイルへの書き込みによるログ保存も、どんな制限もなく実現することが難しくないことを既にはっきりと示しています。

Z.U.2ティルは今クラスSdelkaを使用し、一般的にこのクラスのオブジェクトを関数に渡すには、.jsonなどのファイルにそれらを書き込むことが便利ですが、何とか少し疲れました、私は理解したい人は、私は何かでここに助けることができると思います。なんなら、グラフィカルなインターフェースでねじ込むこともできる。


Python + metatrader5 ライブラリを使った取引を強くお勧めします。

 
Mikhael1983 #:

オープンポジションをターミナルにポーリングする方法を紹介しました。すでにポジションがある場合はリクエストを再送信する必要がなく、ない場合は送信するようにコードを書くことを妨げるものは何もないのです。特に、リクエストを送った後にオブジェクトが返ってきて、そのオブジェクトのプロパティに全てが書かれていることで、正常に開いたのかエラーがあるのかが分かり、それを元に行動することができるのです。ここは問題ないです。

もう一度言うが、この命令はすでに実行され、歴史に刻まれている。アクティブポジションではなくなりました。ポジションはすでに開かれていますが、まだ表示されていません。リクエストでは表示されていません。

通常、これは非常に短い時間間隔であり、特に到達するのは困難です。

しかし、このような状況は、ギャップと大量の注文がある朝のオープニングでは、数秒どころか数分も続くことがあります。

TRADE_TRANSACTION_ORDER_DELETEはTRADE_TRANSACTION_HISTORY_ADDやTRADE_TRANSACTION_DEAL_ADDより先に来ていることを明確にするため。

また、ある時点でアクティブや履歴に注文が表示されなくなったり、注文や取引が表示されなくなったりすることがあります。