305 lines
29 KiB
MQL5
305 lines
29 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Database.mqh |
|
|
//| GrayMatrix |
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Jay Davis"
|
|
#property link "https://512jay.github.io"
|
|
#property version "1.00"
|
|
input string DatabaseName = "DATABASE"; //\AppData\Roaming\MetaQuotes\Terminal\Common\DATABASE
|
|
struct DBMqlTradeRequest
|
|
{
|
|
ENUM_TRADE_REQUEST_ACTIONS action; // Trade operation type
|
|
ulong magic; // Expert Advisor ID (magic number)
|
|
ulong order; // Order ticket
|
|
string symbol; // Trade symbol
|
|
double volume; // Requested volume for a deal in lots
|
|
double price; // Price
|
|
double stoplimit; // StopLimit level of the order
|
|
double sl; // Stop Loss level of the order
|
|
double tp; // Take Profit level of the order
|
|
ulong deviation; // Maximal possible deviation from the requested price
|
|
ENUM_ORDER_TYPE type; // Order type
|
|
ENUM_ORDER_TYPE_FILLING type_filling; // Order execution type
|
|
ENUM_ORDER_TYPE_TIME type_time; // Order expiration type
|
|
datetime expiration; // Order expiration time (for the orders of ORDER_TIME_SPECIFIED type)
|
|
string comment; // Order comment
|
|
ulong position; // Position ticket
|
|
ulong position_by; // The ticket of an opposite position
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Creates a new database if it does not exist |
|
|
//+------------------------------------------------------------------+
|
|
bool CreateDataBase(string filename, bool createNew = false)
|
|
{
|
|
if(!createNew)
|
|
return false;
|
|
Print(__FUNCTION__, " ", filename);
|
|
|
|
//--- open/create the database in the common terminal folder
|
|
int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);
|
|
if(db==INVALID_HANDLE)
|
|
{
|
|
Print("DB: ", filename, " open failed with code ", GetLastError());
|
|
return false;
|
|
}
|
|
//--- create the MqlTradeRequest table
|
|
if(!CreateTableMqlTradeRequest(db, createNew))
|
|
{
|
|
Print("Table created");
|
|
DatabaseClose(db);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Opens the deals table and returns the handle |
|
|
//+------------------------------------------------------------------+
|
|
int OpenDatabase(string filename)
|
|
{
|
|
//--- open/create the database in the common terminal folder
|
|
int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);
|
|
if(db==INVALID_HANDLE)
|
|
{
|
|
Print("DB: ", filename, " open failed with code ", GetLastError());
|
|
return -1;
|
|
}
|
|
return db;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inserts a MqlTradeRequest into DB without a db handle |
|
|
//+------------------------------------------------------------------+
|
|
bool InsertMqlTradeRequest(MqlTradeRequest & request)
|
|
{
|
|
MqlTradeRequest temp = request;
|
|
bool inserted = false;
|
|
int db = OpenDatabase(DatabaseName);
|
|
inserted = InsertMqlTradeRequest(db, request);
|
|
if(!inserted)
|
|
{
|
|
Print("Insert failed taking measures\nCreated database: ", DatabaseName);
|
|
if(!DatabaseTableExists(db, "MQLTRADEREQUEST"))
|
|
if(CreateTableMqlTradeRequest(db, true))
|
|
{
|
|
db = OpenDatabase(DatabaseName);
|
|
inserted = InsertMqlTradeRequest(db, temp);
|
|
}
|
|
}
|
|
DatabaseClose(db);
|
|
return inserted;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Add a request to the database table with handle |
|
|
//+------------------------------------------------------------------+
|
|
bool InsertMqlTradeRequest(int database, MqlTradeRequest & r)
|
|
{
|
|
// --- lock the database before executing transactions
|
|
DatabaseTransactionBegin(database);
|
|
string request_text = StringFormat("INSERT INTO MQLTRADEREQUEST ("
|
|
"ACTION," //%d
|
|
"MAGIC_NUMBER," //%d
|
|
"ORDER_TICKET," //%d
|
|
"SYMBOL," //%s
|
|
"VOLUME," //%G
|
|
"PRICE," //%G
|
|
"STOPLIMIT," //%G
|
|
"STOPLOSS," //%G
|
|
"TAKEPROFIT,"//%G
|
|
"DEVIATION,"//%d
|
|
"TYPE,"//%d
|
|
"TYPE_FILLING,"//%d
|
|
"TYPE_TIME,"//%d
|
|
"EXPIRATION,"//%d
|
|
"COMMENT," //%s.
|
|
"POSITION,"//%d
|
|
"POSITION_BY)"//%d
|
|
"VALUES (%d,%d,%d,'%s',%G,%G,%G,%G,%G,%d,%d,%d,%d,%d,'%s',%d,%d);",
|
|
r.action, r.magic, r.order, r.symbol, r.volume, r.price, r.stoplimit,
|
|
r.sl, r.tp, r.deviation, r.type, r.type_filling, r.type_time,
|
|
r.expiration, r.comment, r.position, r.position_by);
|
|
//Print(r.action, r.magic, r.order, r.symbol, r.volume, r.price, r.stoplimit, r.sl, r.tp, r.comment, r.position, r.position_by, TimeCurrent());
|
|
if(!DatabaseExecute(database, request_text)) //request_text
|
|
{
|
|
PrintFormat("%s: failed to insert MqlTradeRequest %s with code %d",
|
|
__FUNCTION__, r.symbol, GetLastError());
|
|
//--- roll back all transactions and unlock the database
|
|
DatabaseTransactionRollback(database);
|
|
PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
|
|
return(false);
|
|
}
|
|
else
|
|
{
|
|
Print("Inserted: ",
|
|
EnumToString((ENUM_ORDER_TYPE)r.type), " magic #",
|
|
r.magic, " \"", r.comment, "\" on ", r.symbol, " with (vol ", r.volume,
|
|
") at (price:", r.price, ") (SL:", r.sl, ") (TP:", r.tp, ") at ", TimeCurrent());
|
|
}
|
|
//--- all transactions have been performed successfully - record changes and unlock the database
|
|
DatabaseTransactionCommit(database);
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Create the MqlTradeRequest table |
|
|
//+------------------------------------------------------------------+
|
|
bool CreateTableMqlTradeRequest(int database, bool createNew = false)
|
|
{
|
|
//--- if the MQLTRADEREQUEST table already exists, delete it
|
|
if(createNew && !DeleteTable(database, "MQLTRADEREQUEST"))
|
|
{
|
|
return(false);
|
|
}
|
|
//--- check if the table exists
|
|
if(!DatabaseTableExists(database, "MQLTRADEREQUEST"))
|
|
//--- create the table
|
|
if(!DatabaseExecute(database, "CREATE TABLE MQLTRADEREQUEST("
|
|
"ACTION INT," // Trade operation type
|
|
"MAGIC_NUMBER INT," // Expert Advisor ID (magic number)
|
|
"ORDER_TICKET INT," // Order ticket
|
|
"SYMBOL CHAR(25)," // Trade symbol
|
|
"VOLUME REAL," // Requested volume for a deal in lots
|
|
"PRICE REAL," // Price
|
|
"STOPLIMIT REAL," // StopLimit level of the order
|
|
"STOPLOSS REAL," // Stop Loss level of the order
|
|
"TAKEPROFIT REAL," // Take Profit level of the order
|
|
"DEVIATION INT," // Maximal possible deviation from the requested price
|
|
"TYPE INT," // Order type
|
|
"TYPE_FILLING INT," // Order execution type
|
|
"TYPE_TIME INT," // Order expiration type
|
|
"EXPIRATION DATETIME," // Order expiration time
|
|
"COMMENT TINYTEXT," // Order comment
|
|
"POSITION INT," // Position ticket
|
|
"POSITION_BY INT);")) // The ticket of an opposite position
|
|
{
|
|
Print("DB: create the MQLTRADEREQUEST table failed with code ", GetLastError());
|
|
return(false);
|
|
}
|
|
//--- the table has been successfully created
|
|
Print("Created MQLTRADEREQUEST table.");
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Delete a table with the specified name from the database |
|
|
//+------------------------------------------------------------------+
|
|
bool DeleteTable(int database, string table_name)
|
|
{
|
|
if(!DatabaseExecute(database, "DROP TABLE IF EXISTS "+table_name))
|
|
{
|
|
Print("Failed to drop the DEALS table with code ", GetLastError());
|
|
return(false);
|
|
}
|
|
//--- the table has been successfully deleted
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Queries the database with given string, returns a request handle |
|
|
//+------------------------------------------------------------------+
|
|
int QueryDatabase(int databaseHandle, string requestText = "SELECT COUNT(*) FROM MQLTRADEREQUEST;")
|
|
{
|
|
int request = DatabasePrepare(databaseHandle, requestText);
|
|
if(request == INVALID_HANDLE)
|
|
{
|
|
Print("DB: ", requestText, " request failed with code ", GetLastError());
|
|
DatabaseClose(databaseHandle);
|
|
return -1;
|
|
}
|
|
return request;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the number of rows in the given table |
|
|
//+------------------------------------------------------------------+
|
|
int GetTableSize(string databaseName, string table = "MQLTRADEREQUEST")
|
|
{
|
|
int db = OpenDatabase(databaseName);
|
|
string requestText = StringFormat("SELECT COUNT(*) FROM %s;", table);
|
|
int request=QueryDatabase(db, requestText);
|
|
// get table size
|
|
int size;
|
|
DatabaseRead(request);
|
|
if(!DatabaseColumnInteger(request, 0, size))
|
|
{
|
|
Print("DatabaseRead() failed with code ", GetLastError());
|
|
DatabaseFinalize(request);
|
|
DatabaseClose(db);
|
|
return -1;
|
|
}
|
|
//--- remove the query after use
|
|
DatabaseFinalize(request);
|
|
DatabaseClose(db);
|
|
return size;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns true if the database has more updates |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckForDB_Update(long & rowId, string databaseName, string table = "MQLTRADEREQUEST")
|
|
{
|
|
long currentRowID, db;
|
|
db = OpenDatabase(databaseName);
|
|
currentRowID = GetTableSize(databaseName, table);
|
|
if(currentRowID > rowId)
|
|
return true;
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Downloads the latest updates from the database |
|
|
//+------------------------------------------------------------------+
|
|
bool GetDB_Updates(long &rowID, string databaseName, MqlTradeRequest &array[], string table = "MQLTRADEREQUEST")
|
|
{
|
|
DBMqlTradeRequest temp;
|
|
MqlTradeRequest fromDB[];
|
|
int db = OpenDatabase(databaseName);
|
|
string requestText = StringFormat("SELECT * FROM %s WHERE ROWID > %d;", table, rowID);
|
|
int request = QueryDatabase(db, requestText);
|
|
//Print(databaseName, " ", requestText, " handle of a request ", request);
|
|
for(int i=0; DatabaseReadBind(request, temp); i++)
|
|
{
|
|
int size = ArraySize(fromDB);
|
|
ArrayResize(fromDB, size + 1);
|
|
fromDB[i].action = (ENUM_TRADE_REQUEST_ACTIONS) temp.action;
|
|
fromDB[i].comment = temp.comment;
|
|
fromDB[i].deviation = temp.deviation;
|
|
fromDB[i].expiration = temp.expiration;
|
|
fromDB[i].magic = temp.magic;
|
|
fromDB[i].order = temp.order;
|
|
fromDB[i].position = temp.position;
|
|
fromDB[i].position_by = temp.position_by;
|
|
fromDB[i].price = temp.price;
|
|
fromDB[i].sl = temp.sl;
|
|
fromDB[i].stoplimit = temp.stoplimit;
|
|
fromDB[i].tp = temp.tp;
|
|
fromDB[i].type = (ENUM_ORDER_TYPE) temp.type;
|
|
fromDB[i].type_filling = (ENUM_ORDER_TYPE_FILLING) temp.type_filling;
|
|
fromDB[i].type_time = (ENUM_ORDER_TYPE_TIME) temp.type_time;
|
|
fromDB[i].volume = temp.volume;
|
|
fromDB[i].symbol = temp.symbol;
|
|
}
|
|
//--- delete request after use
|
|
DatabaseFinalize(request);
|
|
DatabaseClose(db);
|
|
|
|
int size = ArraySize(fromDB);
|
|
rowID += size;
|
|
ArrayFree(array);
|
|
for(int i=0; i < size; i++)
|
|
{
|
|
ArrayResize(array, ArraySize(array) + 1);
|
|
array[i].action = fromDB[i].action; // = (ENUM_TRADE_REQUEST_ACTIONS) temp.action;
|
|
array[i].comment = fromDB[i].comment; // = temp.comment;
|
|
array[i].deviation = fromDB[i].deviation; // = temp.deviation;
|
|
array[i].expiration = fromDB[i].expiration; // = temp.expiration;
|
|
array[i].magic = fromDB[i].magic; // = temp.magic;
|
|
array[i].order = fromDB[i].order; // = temp.order;
|
|
array[i].position = fromDB[i].position; // = temp.position;
|
|
array[i].position_by = fromDB[i].position_by; // = temp.position_by;
|
|
array[i].price = fromDB[i].price; // = temp.price;
|
|
array[i].sl = fromDB[i].sl; // = temp.sl;
|
|
array[i].stoplimit = fromDB[i].stoplimit; // = temp.stoplimit;
|
|
array[i].tp = fromDB[i].tp; // = temp.tp;
|
|
array[i].type = fromDB[i].type; // = (ENUM_ORDER_TYPE) temp.type;
|
|
array[i].type_filling = fromDB[i].type_filling; // = (ENUM_ORDER_TYPE_FILLING) temp.type_filling;
|
|
array[i].type_time = fromDB[i].type_time; // = (ENUM_ORDER_TYPE_TIME) temp.type_time;
|
|
array[i].volume = fromDB[i].volume; // = temp.volume;
|
|
array[i].symbol = fromDB[i].symbol; // = temp.symbol;
|
|
}
|
|
return size > 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|