mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-28 11:07:48 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 077bec66fa | |||
| 38a7b5f103 | |||
| 060591ff3a | |||
| 88cb70285c | |||
| 3a05fc3353 | |||
| acd8514c38 | |||
| 88dfa84776 | |||
| 8438f0c94b | |||
| f48521cd1d | |||
| 80703b1386 | |||
| 2b9083c342 | |||
| 8369788133 |
@@ -0,0 +1,463 @@
|
||||
#!/usr/bin/env python3
|
||||
# con2mtapi.py - An example for connecting to the mtapi EA on MT4 and get some data
|
||||
# -*- coding: utf-8 -*-
|
||||
# ---------------------------------------------------------------------
|
||||
# Author: EAML
|
||||
# Date: 2020-11-10
|
||||
# Version: 1.0.2
|
||||
# License: MIT
|
||||
# URL: https://github.com/eabase/mt4pycon/
|
||||
# ---------------------------------------------------------------------
|
||||
#
|
||||
# Description:
|
||||
#
|
||||
# This is an example client using the MT4 API (mtapi) to connect
|
||||
# to the MT4 EA, using either an TCP/IP or a pipe port, to get some
|
||||
# historical candle OHLCV data for a specified symbol and timeframe.
|
||||
#
|
||||
# ToDo:
|
||||
#
|
||||
# [ ] Make a native pip installer package (PyPi)
|
||||
# [ ] Automatic detection of MT5
|
||||
# [ ] Fix bug when requested chart does not (yet) have any candle data... double run!
|
||||
# [x] Allow TF specification as MT4/5 text ('4H') and not in minutes ('240')
|
||||
# [ ] Add CLI options:
|
||||
# ----------------------------------------------------------
|
||||
# - [ ] '-5' : Use script for MT5 installs (this will use MT5 DLL's)
|
||||
# - [ ] '-c' : Enable ANSI color markup of output
|
||||
# - [x] '-d' : Enable extra debug info
|
||||
# - [ ] '-e' : Show candle data as minimal CSV list (ignores -l) ???
|
||||
# - [x] '-h' : Show THIS help list
|
||||
# - [x] '-n <n>' : Show <n> number of candles back ['0' is last complete candle]
|
||||
# - [x] '-p' : Use a named pipe connection (instead of a TCP/IP host:port)
|
||||
# - [x] '-l' : Show candle data as a list (one line per OHLC item)
|
||||
# - [x] '-s <symbol>' : Set symbol name to <symbol> ['EURUSD']
|
||||
# - [x] '-t <timeframe>' : Set timeframe to <timeframe>
|
||||
# - [x] '-v' : Show THIS program version
|
||||
# ----------------------------------------------------------
|
||||
#
|
||||
# Installation:
|
||||
#
|
||||
# <TBA>
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# <TBA>
|
||||
#
|
||||
# Requirements:
|
||||
#
|
||||
# pip install pythonnet
|
||||
# pip install
|
||||
#
|
||||
# NOTES
|
||||
#
|
||||
# 1. Spread for candles are meaningless, unless we talk about an "average",
|
||||
# which is not available without also getting all the ticks inside.
|
||||
#
|
||||
# 2. Some Default Settings:
|
||||
# For MT5:
|
||||
# MTPATH = C:\Program Files\MtApi5 # Default Path to installed MtApi library files
|
||||
# MTSERV = 127.0.0.1 # Default IP address of your localhost where MtApi terminal is running
|
||||
# MTPORT = 8228 # Default Port setting of your MtApi EA
|
||||
#
|
||||
# 3. Using TCP vs. a "Named Pipe" connection:
|
||||
# TCP: client.BeginConnect(ip, port)
|
||||
# Pipe: client.BeginConnect(port)
|
||||
# (Note that a Windows "named pipe" is using the "localhost" interface.)
|
||||
#
|
||||
# 4. What is the deal with: 0.0.0.0 & 127.0.0.1 ?
|
||||
# See [4]
|
||||
#
|
||||
# 5. To measure performance, use:
|
||||
# (Measure-Command { python.exe .\con2mtapi.py -n10000 -s "CADCHF" -t 15 -p }).TotalSeconds
|
||||
#
|
||||
# 6. How to get Tick data streamed?
|
||||
#
|
||||
# struct MqlTick {
|
||||
# datetime time; // Time of the last prices update
|
||||
# double bid; // Current Bid price
|
||||
# double ask; // Current Ask price
|
||||
# double last; // Price of the last deal (Last)
|
||||
# ulong volume; // Volume for the current Last price
|
||||
#
|
||||
# REFERENCES
|
||||
#
|
||||
# [1] https://www.mql5.com/en/docs/constants/structures/mqlrates
|
||||
# [2] https://docs.mql4.com/constants/structures/mqltick
|
||||
# [3] https://stackoverflow.com/questions/48542644/python-and-windows-named-pipes
|
||||
# [4] https://www.howtogeek.com/225487/what-is-the-difference-between-127.0.0.1-and-0.0.0.0/
|
||||
# [5] https://docs.mql4.com/constants/chartconstants/enum_timeframes
|
||||
#
|
||||
# ---------------------------------------------------------------------
|
||||
import os, sys
|
||||
import clr
|
||||
import time
|
||||
import getopt
|
||||
|
||||
#--------------------------------------
|
||||
# For MT5 installs
|
||||
#--------------------------------------
|
||||
# C:\Program Files\MtApi5
|
||||
#sys.path.append(r"C:\Program Files\MtApi5")
|
||||
#asm = clr.AddReference("MtApi5")
|
||||
#import MtApi5 as mt
|
||||
|
||||
#--------------------------------------
|
||||
# For MT4 installs
|
||||
#--------------------------------------
|
||||
# C:\Program Files (x86)\MtApi
|
||||
sys.path.append(r"C:\Program Files\MtApi")
|
||||
asm = clr.AddReference('MtApi')
|
||||
import MtApi as mt
|
||||
|
||||
#--------------------------------------
|
||||
# Import/Use colorama
|
||||
#--------------------------------------
|
||||
##from colorama import Fore, Style
|
||||
#from colorama import init, AnsiToWin32
|
||||
#init(wrap=False)
|
||||
#stream = AnsiToWin32(sys.stderr).stream
|
||||
#--------------------------------------
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Global Variables
|
||||
#----------------------------------------------------------
|
||||
__author__ = 'EAML (EABASE)'
|
||||
__copyright__ = 'MIT (2020)'
|
||||
__version__ = '1.0.2'
|
||||
|
||||
debug = 0 # Enable debug printing
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Default Program Constants
|
||||
#----------------------------------------------------------
|
||||
#MTSERV = '192.168.1.101' # IP address of "your local machine" where MtApi terminal is running
|
||||
MTSERV = '127.0.0.1' # IP address of "your localhost" where MtApi terminal is running
|
||||
#MTSERV = '0.0.0.0' # IP address of "ALL on local net" where MtApi terminal is running
|
||||
MTPORT = 8222 # Port setting of your MT4 EA [Default:8222]
|
||||
#MTPORT = 8228 # Port setting of your MT5 EA [Default:8228]
|
||||
MTSYMB = 'EURUSD.e' # <select your default symbol>
|
||||
#MTTIME = 'M15' # <select your default timefrme>
|
||||
|
||||
#------------------------------------------------
|
||||
# mt.ENUM_TIMEFRAMES.PERIOD_M15
|
||||
#------------------------------------------------
|
||||
t1 = '[M1,M5,M15,M30,H1,H4,D1,W1,MN1]' # Standard Periods
|
||||
t2 = '[M2,M3,M4,M6,M10,M12,M20,H2,H3,H6,H8,H12]' # Extended Periods (MQL/API only)
|
||||
p1 = [1,5,15,30,60,240,1440,10080,43200] # [min]
|
||||
p2 = [2,3,4,6,10,12,20,120,180,360,480,720] # [min]
|
||||
|
||||
z1 = t1.translate(str.maketrans('','','[]')).split(',') # Remove "[" and "]"
|
||||
z2 = t2.translate(str.maketrans('','','[]')).split(',') #
|
||||
d1 = dict(zip(z1,p1)) # convert to dict
|
||||
d2 = dict(zip(z2,p2)) # convert to dict
|
||||
d3 = {**d1,**d2} # Add the 2 dicts (in Py3.9+ use: d3=d1|d2)
|
||||
|
||||
d4 = {k: v for k, v in sorted(d3.items(), key=lambda item: item[1])} # sort dict
|
||||
|
||||
# "reverse" the dict key:value pairs, so we can use
|
||||
# get('value') to obtain the key of the original
|
||||
#d4r = dict(zip(d4.values(),d4.keys()))
|
||||
|
||||
#TF = d4.get('M15')
|
||||
# Get the key, given a value
|
||||
#TFk = [k for k in d4 if (d4[k] == TF)][0]
|
||||
#TFk = next((k for k in d4 if d4[k] == TF), None)
|
||||
|
||||
#------------------------------------------------
|
||||
# Text Coloring
|
||||
#------------------------------------------------
|
||||
# Usage: print(yellow("This is yellow"))
|
||||
def color(text, color_code):
|
||||
#if self.nposix:
|
||||
#if not is_posix():
|
||||
# return text
|
||||
# for brighter colors, use "1;" in front of "color_code"
|
||||
bright = '' # '1;'
|
||||
return '\x1b[%s%sm%s\x1b[0m' % (bright, color_code, text)
|
||||
|
||||
def red(text): return color(text, 31) # # noqa
|
||||
def green(text): return color(text, 32) # '1;49;32' # noqa
|
||||
def bgreen(text): return color(text, '1;49;32') # bright green # noqa
|
||||
def orange(text): return color(text, '0;49;91') # 31 - looks bad! # noqa
|
||||
def yellow(text): return color(text, 33) # # noqa
|
||||
def blue(text): return color(text, '1;49;34') # bright blue # noqa
|
||||
def purple(text): return color(text, 35) # aka. magenta # noqa
|
||||
def cyan(text): return color(text, '0;49;96') # 36 # noqa
|
||||
def white(text): return color(text, '0;49;97') # bright white # noqa
|
||||
|
||||
#------------------------------------------------
|
||||
# Print Usage
|
||||
#------------------------------------------------
|
||||
def usage():
|
||||
myName = os.path.basename(__file__)
|
||||
print('\n Usage: ./{}\n'.format(myName))
|
||||
print(' This connects to an MT4 EA via a TCP port (or pipe) to receive OHLCV data.\n')
|
||||
# [:cdhprvn:s:t:]
|
||||
print(' ','-'*80)
|
||||
print(' Command Line Options:')
|
||||
# print(' -5 : Use script for MT5 installs (this will use MT5 DLL\'s)')
|
||||
# print(' -c : Enable ANSI color markup of output')
|
||||
print(' -d : Enable extra debug info')
|
||||
#print(' -e : Show candle data as minimal CSV list (ignores -l)') # ???
|
||||
print(' -n <n> : Show <n> number of candles back')
|
||||
print(' -p : Use a "Named Pipe" connection (instead of a TCP IP host:port) ')
|
||||
print(' -l : Show candle data as a list (one line per OHLC item)')
|
||||
print(' -s <symbol> : Set symbol name to <symbol> ["EURUSD"]')
|
||||
print(' -t <timeframe> : Set timeframe to <timeframe> [M1,M5,M15*,M30,H1,H4,D1,W1,MN1]**')
|
||||
print(' -h, --help : Show THIS help list')
|
||||
print(' -v, --version : Show THIS program version')
|
||||
print(' ','-'*80)
|
||||
print(' * = a default setting')
|
||||
print(' ** = The standard MT4 TF\'s are:')
|
||||
print(' [M1,M5,M15,M30,H1,H4,D1,W1,MN1]')
|
||||
print(' [1,5,15,30,60,240,1440,10080,43200]\n')
|
||||
print(' The non-standard TF\'s are: (not yet available)')
|
||||
print(' [M2,M3,M4,M6,M10,M12,M20,H2,H3,H6,H8,H12]')
|
||||
print(' [2,3,4,6,10,12,20,120,180,360,480,720] ')
|
||||
print(' ','-'*80)
|
||||
print('\n Example for Windows:')
|
||||
#print(' python.exe .\\{} -n4 -s "CADCHF.x" -t 240 -p -d\n'.format(myName))
|
||||
print(' python.exe .\\{} -n4 -s "CADCHF.x" -t H4 -p -d\n'.format(myName))
|
||||
print(' Please file any bug reports at:')
|
||||
print(' https://github.com/eabase/mt4pycon/\n')
|
||||
print(' For support of the MT4/5 API, see:')
|
||||
print(' https://github.com/vdemydiuk/mtapi/\n')
|
||||
print(' Version: {}'.format(__version__))
|
||||
print(' License: {}\n'.format(__copyright__))
|
||||
sys.exit(2)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Helper Functions
|
||||
#----------------------------------------------------------
|
||||
# Test if a given TF argument is a integer
|
||||
def isArgInt(a) :
|
||||
try:
|
||||
int(a)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def printTFerr(a,b) :
|
||||
print(" ERROR: Bad TF argument: {} reverting to default: {} min.".format(a,b))
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Description:
|
||||
# Get history data of MqlRates structure of a specified symbol-period
|
||||
# in specified quantity into the ratesArray array. The elements ordering
|
||||
# of the copied data is from present to the past, i.e., starting
|
||||
# position of 0 means the current bar.
|
||||
#
|
||||
# NOTE:
|
||||
#
|
||||
# The CopyRates() implementations are different between MT4 and MT5.
|
||||
#
|
||||
# [1] https://docs.mql4.com/series/copyrates
|
||||
# [2] https://www.mql5.com/en/docs/series/copyrates
|
||||
#
|
||||
# MT5: int CopyRates(string symbolName, ENUM_TIMEFRAMES timeframe, int startPos, int count, out MqlRates[] ratesArray)
|
||||
# MT4: List<MqlRates> CopyRates(string symbolName, ENUM_TIMEFRAMES timeframe, int startPos, int count)
|
||||
#----------------------------------------------------------
|
||||
def getCopyRates(count,SYM,TF,useList,useCSV):
|
||||
|
||||
startPos = 1 # Starting position of candle (backward counting up!)
|
||||
#count = 3 # Number of candles requested (to be received into Rate Array)
|
||||
rA = [] # The "Rate Array" for Receiving results
|
||||
ccnt = 0 # Number of candles actually recived (into array)
|
||||
errn = 0 # The GetLastError "code" (supposedly?)
|
||||
|
||||
try:
|
||||
# Check how many candles are available to download:
|
||||
#cc = c.iBars(SYM,TF)
|
||||
# if cc < count : print(' INFO: CopyRates: Not all candles are available!');
|
||||
|
||||
#ccnt = client.CopyRates(SYM, TF, startPos, count, rA) # MT5: int
|
||||
rA = client.CopyRates(SYM, TF, startPos, count) # MT4: List<MqlRates>
|
||||
ccnt = len(rA) # Rate Array length
|
||||
|
||||
except Exception as e:
|
||||
print('\n ERROR: in CopyRates(): \n {}\n'.format(e))
|
||||
if debug :
|
||||
print(' DBG: CopyRates:ccnt = ', ccnt)
|
||||
errn = client.GetLastError()
|
||||
errd = client.ErrorDescription(errn) if errn else '<n/a>';
|
||||
print(' DBG: CopyRates: ErrorCode : {:d}'.format(errn))
|
||||
print(' DBG: CopyRates: ErrorDescription :\n {}'.format(errd))
|
||||
exit()
|
||||
|
||||
errn = client.GetLastError();
|
||||
#TFk = next((k for k in d4 if d4[k] == TF), None)
|
||||
TFk = [k for k in d1 if (d1[k] == TF)][0]
|
||||
|
||||
#if debug :
|
||||
print()
|
||||
print(' ','-'*60)
|
||||
print(' INFO: CopyRates: Symbol : {}'.format(SYM))
|
||||
print(' INFO: CopyRates: TimeFrame : {}'.format(TFk))
|
||||
print(' INFO: CopyRates: Requested : {:d} candles'.format(count))
|
||||
print(' INFO: CopyRates: Received : {:d} candles'.format(ccnt))
|
||||
print(' INFO: CopyRates: ErrorCode : {:d}'.format(errn)) # rA[0].ErrorCode))
|
||||
print(' ','-'*60)
|
||||
print(' INFO: CopyRates: Rate Array (rA):\n')
|
||||
|
||||
#--------------------------------------------
|
||||
# Response = {"ErrorCode" : 0, "Rates" :
|
||||
# [{ "Close" : 1.17094,
|
||||
# "Open" : 1.17206,
|
||||
# "MtTime" : 1604432700,
|
||||
# "Low" : 1.17079,
|
||||
# "High" : 1.17207,
|
||||
# "TickVolume" : 1251,
|
||||
# "RealVolume" : 0,
|
||||
# "Spread" : 0}, ...
|
||||
#--------------------------------------------
|
||||
# We skip: rA[i].Spread (see notes)
|
||||
#--------------------------------------------
|
||||
|
||||
if useCSV :
|
||||
csv_th = 'Time,Open,High,Low,Close,Volume'
|
||||
csv_tf = '{},{:.5f},{:.5f},{:.5f},{:.5f},{:d}'
|
||||
print(csv_th)
|
||||
for i in range(ccnt):
|
||||
print(csv_tf.format(rA[i].Time, rA[i].Open, rA[i].High, rA[i].Low, rA[i].Close, rA[i].TickVolume))
|
||||
sys.exit(2)
|
||||
|
||||
if useList :
|
||||
myLformat = '[{}]: {}\nO: {}\nH: {}\nL: {}\nC: {}\nV: {:d}\n' # S: {:.2f}
|
||||
for i in range(ccnt):
|
||||
print(myLformat.format(i, rA[i].Time, rA[i].Open, rA[i].High, rA[i].Low, rA[i].Close, rA[i].TickVolume))
|
||||
else :
|
||||
tableHeader = ' {:<4} {:<19} {:<7} {:<7} {:<7} {:<7} {:<6}'
|
||||
header_str = tableHeader.format('#','Time','Open', 'High', 'Low', 'Close', 'Volume')
|
||||
# 0: 2020-11-04 01:45:00 1.17531 1.17616 1.17500 1.17613 590
|
||||
hlen = len(header_str)
|
||||
print(header_str)
|
||||
print(' ','-'*hlen)
|
||||
|
||||
myRformat = ' {:<3}: {} {:.5f} {:.5f} {:.5f} {:.5f} {:d}'
|
||||
for i in range(ccnt):
|
||||
print(myRformat.format(i, rA[i].Time, rA[i].Open, rA[i].High, rA[i].Low, rA[i].Close, rA[i].TickVolume))
|
||||
|
||||
#----------------------------------------------------------
|
||||
# ToDo
|
||||
#----------------------------------------------------------
|
||||
#def getLiveTicks():
|
||||
# Here be dragons
|
||||
# SymbolInfoTick
|
||||
# MqlTick tick;
|
||||
# if (!SymbolInfoTick(symbol, tick)) {
|
||||
# response = CreateErrorResponse(GetLastError(), "SymbolInfoDouble failed");
|
||||
# return;
|
||||
# }
|
||||
#SymbolInfoTick
|
||||
|
||||
#----------------------------------------------------------
|
||||
# MAIN
|
||||
#----------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
#def main(self):
|
||||
|
||||
#----------------------------------------------------------
|
||||
# CLI arguments
|
||||
#----------------------------------------------------------
|
||||
dTF = d1.get('M15')
|
||||
nmax = 3 # Default number of candles
|
||||
mtVer = 4 # Default to MT4 API version [4,5]
|
||||
aSym = str(MTSYMB) # Default Symbol()
|
||||
aTF = dTF # Default TimeFrame [Default: M15]
|
||||
apipe = 0 # Use a Named Pipe port for connection (instead of TCP host:port)
|
||||
alist = 0 #
|
||||
useCSV = 0 # ... CSV
|
||||
|
||||
narg = len(sys.argv) - 1
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], ":cdehpvln:s:t:", ["help", "version"])
|
||||
except getopt.GetoptError :
|
||||
usage(); sys.exit();
|
||||
|
||||
if not opts :
|
||||
if not args :
|
||||
usage(); sys.exit();
|
||||
else :
|
||||
for opt, arg in opts:
|
||||
if (debug) :
|
||||
print ("opt: ", opt)
|
||||
print ("arg: ", arg)
|
||||
|
||||
if opt in ("-h", "--help"): usage(); sys.exit();
|
||||
elif opt in ("-v", "--version"): print ("Version: ", __version__); sys.exit();
|
||||
#elif opt == "-c": useColors=1; # Use colored markup on: (Time, Volume, OHCL)
|
||||
elif opt == "-d": debug = 1; #
|
||||
elif opt == "-e": useCSV = 1; # print OHLCV data as CSV
|
||||
elif opt == "-l": alist = 1; # print each OHLCV on new line
|
||||
elif opt == "-p": apipe = 1; # use named pipe connection
|
||||
elif opt == "-n": nmax = int(arg); # Number of received candles
|
||||
elif opt == "-s": aSym = str(arg); # Instrument Symbol
|
||||
elif opt == "-t": # <timeframe>
|
||||
if (isArgInt(arg)) : #
|
||||
if int(arg) in d1.values() : # check if '30' in d1
|
||||
aTF = int(arg) # convert '30' to 30
|
||||
else :
|
||||
printTFerr(arg,dTF)
|
||||
continue
|
||||
else:
|
||||
if arg in d1.keys() : # check if 'M30' in d1
|
||||
aTF = d1.get(arg) # get 30 from 'M30'
|
||||
else :
|
||||
printTFerr(arg,dTF)
|
||||
continue
|
||||
#----------------------------------------------------------
|
||||
|
||||
#client = mt.MtApi5Client()
|
||||
client = mt.MtApiClient()
|
||||
|
||||
ip = str(MTSERV) # Host IP
|
||||
port = MTPORT # EA port
|
||||
sleeps = 10 # Sleep this many times
|
||||
cnt = 1 #
|
||||
|
||||
print('\n INFO: Attmpting to Connect to: {}:{}'.format(ip,port))
|
||||
if (debug): print(' DBG : using {}'.format('a name pipe' if apipe else 'TCP IP'))
|
||||
|
||||
try:
|
||||
while sleeps > 0:
|
||||
|
||||
print(' [{}] Connecting '.format(cnt), end='')
|
||||
if (apipe == 0) :
|
||||
client.BeginConnect(ip, port)
|
||||
else :
|
||||
client.BeginConnect(port)
|
||||
|
||||
time.sleep(1)
|
||||
sleeps = sleeps - 1
|
||||
cnt += 1
|
||||
|
||||
#------------------------------------
|
||||
# Check Connection state:
|
||||
#------------------------------------
|
||||
# 0 = Disconnected
|
||||
# 1 = Connecting
|
||||
# 2 = Connected
|
||||
# 3 = Failed
|
||||
#------------------------------------
|
||||
if client.ConnectionState == 0: print('Disconnected'); continue;
|
||||
if client.ConnectionState == 1: print('.', end=''); continue;
|
||||
if client.ConnectionState == 2: print('OK'); getCopyRates(nmax,aSym,aTF,alist,useCSV); break
|
||||
if client.ConnectionState == 3: print('FAILED'); break
|
||||
|
||||
if (debug): print(' DBG: Unknown ConnectionState: ', client.ConnectionState)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
client.BeginDisconnect()
|
||||
|
||||
if (debug): print('\n DBG: Disconnected!')
|
||||
print('\n Done.')
|
||||
|
||||
#----------------------------------------------------------
|
||||
# END
|
||||
#----------------------------------------------------------
|
||||
#if __name__ == "__main__":
|
||||
# main()
|
||||
# sys.exit(0)
|
||||
@@ -37,6 +37,7 @@ template <typename T> T Execute(std::function<T()> func, wchar_t* err, T default
|
||||
catch (Exception^ e)
|
||||
{
|
||||
convertSystemString(err, e->Message);
|
||||
MtAdapter::GetInstance()->LogError(e->Message);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -167,20 +168,20 @@ _DLLAPI bool _stdcall getCommandType(int expertHandle, int* res, wchar_t* err)
|
||||
}, err, false);
|
||||
}
|
||||
|
||||
// --- index parameters
|
||||
|
||||
_DLLAPI bool _stdcall getIntValue(int expertHandle, int paramIndex, int* res, wchar_t* err)
|
||||
{
|
||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||
*res = (int)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||
*res = MtAdapter::GetInstance()->GetCommandParameter<int>(expertHandle, paramIndex);
|
||||
return true;
|
||||
}, err, false);
|
||||
}
|
||||
|
||||
// --- index parameters
|
||||
|
||||
_DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* res, wchar_t* err)
|
||||
{
|
||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||
*res = (double)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||
*res = MtAdapter::GetInstance()->GetCommandParameter<double>(expertHandle, paramIndex);
|
||||
return true;
|
||||
}, err, false);
|
||||
}
|
||||
@@ -188,7 +189,7 @@ _DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* r
|
||||
_DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t* res, wchar_t* err)
|
||||
{
|
||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||
convertSystemString(res, (String^)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex));
|
||||
convertSystemString(res, MtAdapter::GetInstance()->GetCommandParameter<String^>(expertHandle, paramIndex));
|
||||
return true;
|
||||
}, err, false);
|
||||
}
|
||||
@@ -196,7 +197,7 @@ _DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t*
|
||||
_DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res, wchar_t* err)
|
||||
{
|
||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||
*res = (bool)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||
*res = MtAdapter::GetInstance()->GetCommandParameter<bool>(expertHandle, paramIndex);
|
||||
return true;
|
||||
}, err, false);
|
||||
}
|
||||
@@ -204,7 +205,7 @@ _DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res
|
||||
_DLLAPI bool _stdcall getLongValue(int expertHandle, int paramIndex, __int64* res, wchar_t* err)
|
||||
{
|
||||
return Execute<bool>([&expertHandle, ¶mIndex, res]() {
|
||||
*res = (__int64)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex);
|
||||
*res = MtAdapter::GetInstance()->GetCommandParameter<__int64>(expertHandle, paramIndex);
|
||||
return true;
|
||||
}, err, false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using MtApi.Events;
|
||||
|
||||
namespace MtApi
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MtApi
|
||||
namespace MtApi.Events
|
||||
{
|
||||
internal class MtChartEvent
|
||||
{
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MtApi.Events
|
||||
{
|
||||
public enum MtEventTypes
|
||||
{
|
||||
LastTimeBar = 1,
|
||||
ChartEvent = 2,
|
||||
OnLockTicks = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace MtApi.Events
|
||||
{
|
||||
internal class OnLockTicksEvent
|
||||
{
|
||||
public string Instrument { get; set; }
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -74,6 +74,7 @@
|
||||
<Compile Include="EnumSymbolInfoInteger.cs" />
|
||||
<Compile Include="EnumTerminalInfoDouble.cs" />
|
||||
<Compile Include="EnumTerminalInfoInteger.cs" />
|
||||
<Compile Include="Events\OnLockTicksEvent.cs" />
|
||||
<Compile Include="FlagFontStyle.cs" />
|
||||
<Compile Include="Monitors\AvailabilityOrdersEventArgs.cs" />
|
||||
<Compile Include="Monitors\OrderModification\ModifiedOrdersEventArgs.cs" />
|
||||
@@ -86,14 +87,15 @@
|
||||
<Compile Include="Monitors\Triggers\TimeElapsedTrigger.cs" />
|
||||
<Compile Include="MqlRates.cs" />
|
||||
<Compile Include="MqlTick.cs" />
|
||||
<Compile Include="MtChartEvent.cs" />
|
||||
<Compile Include="Events\MtChartEvent.cs" />
|
||||
<Compile Include="MtConnectionEventArgs.cs" />
|
||||
<Compile Include="MtConnectionException.cs" />
|
||||
<Compile Include="MtConnectionState.cs" />
|
||||
<Compile Include="MtCommandType.cs" />
|
||||
<Compile Include="MtErrorCode.cs" />
|
||||
<Compile Include="MtEventTypes.cs" />
|
||||
<Compile Include="Events\MtEventTypes.cs" />
|
||||
<Compile Include="MtExecutionException.cs" />
|
||||
<Compile Include="MtLockTicksEventArgs.cs" />
|
||||
<Compile Include="MtOrder.cs" />
|
||||
<Compile Include="MtQuote.cs" />
|
||||
<Compile Include="MtQuoteEventArgs.cs" />
|
||||
|
||||
@@ -9,6 +9,7 @@ using MtApi.Requests;
|
||||
using MtApi.Responses;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading.Tasks;
|
||||
using MtApi.Events;
|
||||
|
||||
namespace MtApi
|
||||
{
|
||||
@@ -3140,6 +3141,9 @@ namespace MtApi
|
||||
case MtEventTypes.ChartEvent:
|
||||
FireOnChartEvent(e.ExpertHandle, JsonConvert.DeserializeObject<MtChartEvent>(e.Payload));
|
||||
break;
|
||||
case MtEventTypes.OnLockTicks:
|
||||
FireOnLockTicks(e.ExpertHandle, JsonConvert.DeserializeObject<OnLockTicksEvent>(e.Payload));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
@@ -3155,6 +3159,11 @@ namespace MtApi
|
||||
OnChartEvent?.Invoke(this, new ChartEventArgs(expertHandler, chartEvent));
|
||||
}
|
||||
|
||||
private void FireOnLockTicks(int expertHandler, OnLockTicksEvent lockTicksEvent)
|
||||
{
|
||||
OnLockTicks?.Invoke(this, new MtLockTicksEventArgs(expertHandler, lockTicksEvent.Instrument));
|
||||
}
|
||||
|
||||
private void BacktestingReady()
|
||||
{
|
||||
SendCommand<object>(MtCommandType.BacktestingReady, null);
|
||||
@@ -3171,6 +3180,7 @@ namespace MtApi
|
||||
public event EventHandler<MtConnectionEventArgs> ConnectionStateChanged;
|
||||
public event EventHandler<TimeBarArgs> OnLastTimeBar;
|
||||
public event EventHandler<ChartEventArgs> OnChartEvent;
|
||||
public event EventHandler<MtLockTicksEventArgs> OnLockTicks;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace MtApi
|
||||
{
|
||||
public enum MtEventTypes
|
||||
{
|
||||
LastTimeBar = 1,
|
||||
ChartEvent = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace MtApi
|
||||
{
|
||||
public class MtLockTicksEventArgs : EventArgs
|
||||
{
|
||||
internal MtLockTicksEventArgs(int expertHandle, string symbol)
|
||||
{
|
||||
ExpertHandle = expertHandle;
|
||||
Symbol = symbol;
|
||||
}
|
||||
|
||||
public int ExpertHandle { get; }
|
||||
public string Symbol { get; }
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.42.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.42.0")]
|
||||
[assembly: AssemblyVersion("1.0.43.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.43.0")]
|
||||
@@ -1,27 +1,234 @@
|
||||
# MtApi - .NET API for MetaTrader Trading Platform (MetaQuotes).
|
||||
MtApi provides a .NET API to work with famous trading platfroms MetaTrader4 and MetaTrader5 (MetaQuotes).
|
||||
The API connects directly to the MetaTrader terminal and works with MQL functions. Most functions of the API have MQL interface.
|
||||
The connection can be local or remote (by TCP).
|
||||
|
||||
# MtApi Structure
|
||||
The project has two parts:
|
||||
- client side (C#): MtApi and MtApi5;
|
||||
- server side (C# and C++/CLI): MTApiService, MTConnector, MT5Connector, MQL experts.
|
||||
Server side was designed using WCF framework with the intention of using flexibility to setup connections. One downside of using the WCF framework is the slower speed compared to other connection types (for example, shared memory).
|
||||
MTApiService is a common engine communication project of the API for MT4 and MT5.
|
||||
MTApiService library should be placed in Windows GAC (Global Assembly Cache). Installers in the project will be copied to GAC automatically.
|
||||
|
||||
# How to Build Solution
|
||||
The project is supported by Visual Studio 2017 and requires WIX Tools (http://wixtoolset.org/).
|
||||
|
||||
To make an API for MetaTrader4 use MtApiInstaller and for MetaTrader5 use MtApi5Installer.
|
||||
All installers will be placed in the folder "[root]\build\installers\" and all *.dll files will be placed in "[root]\build\products\".
|
||||
MQL files have been build to ex4 and stored into folders "mq4" for MetaTrader and "mq5" for MetaTrader5. They are ready to be used in terminals.
|
||||
Changing the source code of MQL expert requires recompilation with MetaEditor. Resulting in the need to copy files "hash.mqh" and "json.mqh" to the MetaEditor include folder.
|
||||
|
||||
# Home Website
|
||||
|
||||
# Telegram Channel
|
||||
https://t.me/mtapi4
|
||||
|
||||
https://t.me/joinchat/GfnfUxvelQCLvvIvLO16-w
|
||||
## Introduction
|
||||
MtApi provides a .NET API for working with famous trading platfrom [MetaTrader(MetaQuotes)](https://www.metatrader5.com/).
|
||||
It is not API for connection to MT servers directly. MtApi is just a bridge between MT terminal and .NET applications designed by developers.
|
||||
MtApi executes MQL commands and functions by MtApi's expert linked to chart of MetaTrader.
|
||||
Most of the API's functions duplicates MQL interface.
|
||||
The project was designed using [WCF](https://docs.microsoft.com/en-us/dotnet/framework/wcf/whats-wcf) framework with the intention of using flexibility to setup connections.
|
||||
|
||||
## Build environment
|
||||
The project is supported by Visual Studio 2017.
|
||||
It requires WIX Tools for preparing project's installers (http://wixtoolset.org/).
|
||||
|
||||
Installing WIX for mtapi:
|
||||
1. Make sure you install one of the latest (3.14+) development releases of the wixtoolset.
|
||||
(If you use an older installer you will have to install the ancient .NET 3.5 framework, and that I am sure you will regret, if you do!).
|
||||
2. Run the installer and wait for completion or for asking to also install the VS extensions.
|
||||
|
||||

|
||||
|
||||
3. Install the WiX Toolset Visual Studio Extension depending on your VS version.
|
||||
For example, if you use VS 2017, go [here](https://marketplace.visualstudio.com/items?itemName=WixToolset.WixToolsetVisualStudio2017Extension) or download from their GitHub, releases.
|
||||
|
||||
Use [MetaEditor](https://www.metatrader5.com/en/automated-trading/metaeditor) to working with MQL files.
|
||||
|
||||
## How to Build Solution
|
||||
|
||||
To build the solution for **MT4**, you need to choose the configuration to build for **`x86`**
|
||||
and start with building the **`MtApiInstaller`**. This will build all projects related to MT4:
|
||||
- `MtApi`
|
||||
- `MTApiService`
|
||||
- `MTConnector`
|
||||
|
||||
For building the solution for MT5, you need to choose the configuration to build for **`x64`** (or **`x86`** for the 32-bit MT5)) and start build **`MtApi5Installer`**. This will build all projects related to MT5:
|
||||
- `MtApi5`
|
||||
- `MTApiService`
|
||||
- `MT5Connector`
|
||||
|
||||
All binaries are placed in the project root folder, in the *build* directory: **`../build/`**.
|
||||
The installers (**`*.msi, *.exe`**) will be found under: **`../build/installers/`**.
|
||||
All the DLL library binaries (**`*.dll`**) in: **`../bin/`**.
|
||||
|
||||
MQL files have been pre-compiled to **`*.ex4`** and can be found in the repository here:
|
||||
- **`..\mql4\`**
|
||||
- **`..\mql5\`**
|
||||
|
||||
Changing the source code of the MQL *Expert Advisor* (EA), requires recompilation with *`MetaEditor`*.
|
||||
|
||||
Before you can recompile the EA, you need to add/place the following MQL library files, in the *MetaEditor* **`../Include/`** folder.
|
||||
- **`hash.mqh`**
|
||||
- **`json.mqh`**
|
||||
|
||||
The *`MetaEditor`* *include* folder is usually located here:
|
||||
```xml
|
||||
C:\Users\<username>\AppData\Roaming\MetaQuotes\Terminal\<terminal-hash>\MQL5\Include\.
|
||||
```
|
||||
|
||||
|
||||
## Project Structure
|
||||
|
||||
* `MTApiService`: **`(C#, .dll)`**
|
||||
The common engine communication project of the API. It contains the implementations of client and server sides.
|
||||
|
||||
* `MTConnector, MT5Connector`: **`(C++/CLI, .dll)`**
|
||||
The libraries that are working as proxy between MQL and C# layers. They provides the interfaces.
|
||||
|
||||
* `MtApi, MtApi5`: **`(C#, .dll)`**
|
||||
The client side libraries that are using in user's projects.
|
||||
|
||||
* **`(MQL4/MQL5, .ex4)`**
|
||||
MT4 and MT5 *Expert Advisors* linked to terminal's charts. They executes API's functions and provides trading events.
|
||||
|
||||
* `MtApiInstaller, MtApi5Installer` **`(WIX, .msi)`**
|
||||
The project's installers.
|
||||
|
||||
* `MtApiBootstrapper, MtApi5Bootstrapper` **`(WIX, .exe)`**
|
||||
The installation package bundles. There are wrappers for installers that contains the vc_redist
|
||||
libraries (Visual C++ runtime) placed in [root]\vcredist\.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Use the installers to setup all libraries automatically.
|
||||
- For *MT4*, use: `MtApiInstaller_setup.exe`
|
||||
- For *MT5*, use: `MtApi5Installer setup.exe`
|
||||
|
||||
`MtApiBootstrapper` and `MtApi5Bootstrapper` are installation package bundles that contain the installers and `vc_redist` Windows libraries. The installers place the `MTApiService.dll` into the Windows GAC (*Global Assembly Cache*) and copies `MTConnector.dll` and `MT5Connector.dll` into the Windows's system folder, whose location depend on your Windows OS. After installation, the **`MtApi.ex4`** (or **`MtApi5.ex5`**) EA, must be copied into your Terminal data folder for Expert Advisors, which is normally located in: **`../MQL5/Experts/`**.
|
||||
|
||||
To quickly navigate to the trading platform *data folder*, click: **`File >> "Open data folder"`** in your *MetaTrader* Terminal.
|
||||
|
||||
## Using
|
||||
MtApi provides two types of connection to MetaTrader terminal: local (using Pipe or TCP) and remote (via TCP).
|
||||
The port of connection is defined by MtApi expert.
|
||||
|
||||
Console sample for MT5:
|
||||
```C#
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MtApi5;
|
||||
|
||||
namespace MtApi5Console
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static readonly EventWaitHandle _connnectionWaiter = new AutoResetEvent(false);
|
||||
static readonly MtApi5Client _mtapi = new MtApi5Client();
|
||||
|
||||
static void _mtapi_ConnectionStateChanged(object sender, Mt5ConnectionEventArgs e)
|
||||
{
|
||||
switch (e.Status)
|
||||
{
|
||||
case Mt5ConnectionState.Connecting:
|
||||
Console.WriteLine("Connnecting...");
|
||||
break;
|
||||
case Mt5ConnectionState.Connected:
|
||||
Console.WriteLine("Connnected.");
|
||||
_connnectionWaiter.Set();
|
||||
break;
|
||||
case Mt5ConnectionState.Disconnected:
|
||||
Console.WriteLine("Disconnected.");
|
||||
_connnectionWaiter.Set();
|
||||
break;
|
||||
case Mt5ConnectionState.Failed:
|
||||
Console.WriteLine("Connection failed.");
|
||||
_connnectionWaiter.Set();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _mtapi_QuoteAdded(object sender, Mt5QuoteEventArgs e)
|
||||
{
|
||||
Console.WriteLine("Quote added with symbol {0}", e.Quote.Instrument);
|
||||
}
|
||||
|
||||
static void _mtapi_QuoteRemoved(object sender, Mt5QuoteEventArgs e)
|
||||
{
|
||||
Console.WriteLine("Quote removed with symbol {0}", e.Quote.Instrument);
|
||||
}
|
||||
|
||||
static void _mtapi_QuoteUpdate(object sender, Mt5QuoteEventArgs e)
|
||||
{
|
||||
Console.WriteLine("Quote updated: {0} - {1} : {2}", e.Quote.Instrument, e.Quote.Bid, e.Quote.Ask);
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Application started.");
|
||||
|
||||
_mtapi.ConnectionStateChanged += _mtapi_ConnectionStateChanged;
|
||||
_mtapi.QuoteAdded += _mtapi_QuoteAdded;
|
||||
_mtapi.QuoteRemoved += _mtapi_QuoteRemoved;
|
||||
_mtapi.QuoteUpdate += _mtapi_QuoteUpdate;
|
||||
|
||||
_mtapi.BeginConnect(8228);
|
||||
_connnectionWaiter.WaitOne();
|
||||
|
||||
if (_mtapi.ConnectionState == Mt5ConnectionState.Connected)
|
||||
{
|
||||
Run();
|
||||
}
|
||||
|
||||
Console.WriteLine("Application finished. Press any key...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
private static void Run()
|
||||
{
|
||||
ConsoleKeyInfo cki;
|
||||
do
|
||||
{
|
||||
cki = Console.ReadKey();
|
||||
switch (cki.KeyChar.ToString())
|
||||
{
|
||||
case "b":
|
||||
Buy();
|
||||
break;
|
||||
case "s":
|
||||
Sell();
|
||||
break;
|
||||
}
|
||||
} while (cki.Key != ConsoleKey.Escape);
|
||||
|
||||
_mtapi.BeginDisconnect();
|
||||
_connnectionWaiter.WaitOne();
|
||||
}
|
||||
|
||||
private static async void Buy()
|
||||
{
|
||||
const string symbol = "EURUSD";
|
||||
const double volume = 0.1;
|
||||
MqlTradeResult tradeResult = null;
|
||||
var retVal = await Execute(() => _mtapi.Buy(out tradeResult, volume, symbol));
|
||||
Console.WriteLine($"Buy: symbol EURUSD retVal = {retVal}, result = {tradeResult}");
|
||||
}
|
||||
|
||||
private static async void Sell()
|
||||
{
|
||||
const string symbol = "EURUSD";
|
||||
const double volume = 0.1;
|
||||
MqlTradeResult tradeResult = null;
|
||||
var retVal = await Execute(() => _mtapi.Sell(out tradeResult, volume, symbol));
|
||||
Console.WriteLine($"Sell: symbol EURUSD retVal = {retVal}, result = {tradeResult}");
|
||||
}
|
||||
|
||||
private static async Task<TResult> Execute<TResult>(Func<TResult> func)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
var result = default(TResult);
|
||||
try
|
||||
{
|
||||
result = func();
|
||||
}
|
||||
catch (ExecutionException ex)
|
||||
{
|
||||
Console.WriteLine($"Exception: {ex.ErrorCode} - {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Exception: {ex.Message}");
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
# Telegram Channel
|
||||
https://t.me/mtapi4
|
||||
|
||||
https://t.me/joinchat/GfnfUxvelQCLvvIvLO16-w
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace TestApiClientUI
|
||||
_apiClient.ConnectionStateChanged += apiClient_ConnectionStateChanged;
|
||||
_apiClient.OnLastTimeBar += _apiClient_OnLastTimeBar;
|
||||
_apiClient.OnChartEvent += _apiClient_OnChartEvent;
|
||||
_apiClient.OnLockTicks += _apiClient_OnLockTicks;
|
||||
|
||||
InitOrderCommandsGroup();
|
||||
|
||||
@@ -149,6 +150,14 @@ namespace TestApiClientUI
|
||||
PrintLog(msg);
|
||||
}
|
||||
|
||||
private void _apiClient_OnLockTicks(object sender, MtLockTicksEventArgs e)
|
||||
{
|
||||
var msg =
|
||||
$"OnLockTicks: ExpertHandle = {e.ExpertHandle}, Symbol = {e.Symbol}";
|
||||
Console.WriteLine(msg);
|
||||
PrintLog(msg);
|
||||
}
|
||||
|
||||
private void apiClient_QuoteUpdated(object sender, string symbol, double bid, double ask)
|
||||
{
|
||||
Console.WriteLine(@"Quote: Symbol = {0}, Bid = {1}, Ask = {2}", symbol, bid, ask);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+1
-1
@@ -6685,7 +6685,7 @@ void Execute_TesterStop()
|
||||
{
|
||||
if (!IsTesting())
|
||||
{
|
||||
Print("WARNING: function UnlockTicks can be used only for backtesting");
|
||||
Print("WARNING: function TesterStop can be used only for backtesting");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user