├── .gitignore ├── MQL ├── Include │ ├── MQLMySQL.mqh │ └── Symbols.mqh ├── Indicators │ ├── z4_DATA_MODEL_to_CSV_1.0.mq4 │ ├── z4_DATA_MODEL_to_CSV_Single_1.0.mq4 │ ├── z4_DATA_MODEL_to_MySQL_1.0.mq4 │ └── z4_Symbol_Properties_to_MySQL_1.0.mq4 └── Libraries │ └── MQLMySQL.def ├── README.md ├── readme.txt ├── requirements.txt └── webdav.conf.sample /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | *.pyc 9 | *.gif 10 | *.jpg 11 | *.png 12 | *.jpeg 13 | *.svg 14 | 15 | #data 16 | *.csv 17 | *.mp 18 | *.zip 19 | *.conf 20 | 21 | # Distribution / packaging 22 | .Python 23 | env/ 24 | build/ 25 | develop-eggs/ 26 | <<<<<<< HEAD 27 | Scripts/ 28 | src/ 29 | static/ 30 | migrations/ 31 | ======= 32 | >>>>>>> be9a5c610d84fff55b03e0df46eb28d8fdf9cbad 33 | dist/ 34 | downloads/ 35 | eggs/ 36 | .eggs/ 37 | lib/ 38 | lib64/ 39 | parts/ 40 | sdist/ 41 | var/ 42 | <<<<<<< HEAD 43 | ======= 44 | >>>>>>> be9a5c610d84fff55b03e0df46eb28d8fdf9cbad 45 | *.egg-info/ 46 | .installed.cfg 47 | *.egg 48 | strategies_list.py 49 | strategies.py 50 | 51 | # PyInstaller 52 | # Usually these files are written by a python script from a template 53 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 54 | *.manifest 55 | *.spec 56 | 57 | # Installer logs 58 | pip-log.txt 59 | pip-delete-this-directory.txt 60 | 61 | # Unit test / coverage reports 62 | htmlcov/ 63 | .tox/ 64 | .coverage 65 | .coverage.* 66 | .cache 67 | nosetests.xml 68 | coverage.xml 69 | *,cover 70 | .hypothesis/ 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | 80 | # Flask stuff: 81 | instance/ 82 | .webassets-cache 83 | 84 | # Scrapy stuff: 85 | .scrapy 86 | 87 | # Sphinx documentation 88 | docs/_build/ 89 | 90 | # PyBuilder 91 | target/ 92 | 93 | # IPython Notebook 94 | .ipynb_checkpoints 95 | 96 | # pyenv 97 | .python-version 98 | 99 | # celery beat schedule file 100 | celerybeat-schedule 101 | 102 | # dotenv 103 | .env 104 | 105 | # virtualenv 106 | venv/ 107 | ENV/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | 112 | # Rope project settings 113 | .ropeproject 114 | 115 | #various 116 | *.json 117 | *.pack 118 | *.hdf 119 | *.fth 120 | *.sqlite3 121 | *.pdf 122 | *.pr2 -------------------------------------------------------------------------------- /MQL/Include/MQLMySQL.mqh: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | * MQLMySQL interface library * 3 | ******************************************************************** 4 | * This library uses MQLMySQL.DLL was developed as interface to con-* 5 | * nect to the MySQL database server. * 6 | * Note: Check expert advisor "Common" parameters to be sure that * 7 | * DLL imports are allowed. * 8 | ********************************************************************/ 9 | bool SQLTrace = false; 10 | datetime MySqlLastConnect=0; 11 | 12 | #import "..\libraries\MQLMySQL.dll" 13 | // returns version of MySqlCursor.dll library 14 | string cMySqlVersion (); 15 | 16 | // number of last error of connection 17 | int cGetMySqlErrorNumber(int pConnection); 18 | 19 | // number of last error of cursor 20 | int cGetCursorErrorNumber(int pCursorID); 21 | 22 | // description of last error for connection 23 | string cGetMySqlErrorDescription(int pConnection); 24 | 25 | // description of last error for cursor 26 | string cGetCursorErrorDescription(int pCursorID); 27 | 28 | // establish connection to MySql database server 29 | // and return connection identifier 30 | int cMySqlConnect (string pHost, // Host name 31 | string pUser, // User 32 | string pPassword, // Password 33 | string pDatabase, // Database name 34 | int pPort, // Port 35 | string pSocket, // Socket for Unix 36 | int pClientFlag);// Client flag 37 | // closes connection to database 38 | void cMySqlDisconnect (int pConnection); // pConnection - database identifier (pointer to structure) 39 | // executes non-SELECT statements 40 | bool cMySqlExecute (int pConnection, // pConnection - database identifier (pointer to structure) 41 | string pQuery); // pQuery - SQL query for execution 42 | // creates an cursor based on SELECT statement 43 | // return valuse - cursor identifier 44 | int cMySqlCursorOpen (int pConnection, // pConnection - database identifier (pointer to structure) 45 | string pQuery); // pQuery - SELECT statement for execution 46 | // closes opened cursor 47 | void cMySqlCursorClose (int pCursorID); // pCursorID - internal identifier of cursor 48 | // return number of rows was selected by cursor 49 | int cMySqlCursorRows (int pCursorID); // pCursorID - internal identifier of cursor 50 | // fetch next row from cursor into current row buffer 51 | // return true - if succeeded, otherwise - false 52 | bool cMySqlCursorFetchRow(int pCursorID); // pCursorID - internal identifier of cursor 53 | // retrieves the value from current row was fetched by cursor 54 | string cMySqlGetRowField (int pCursorID, // pCursorID - internal identifier of cursor 55 | int pField); // pField - number of field in SELECT clause (started from 0,1,2... e.t.c.) 56 | 57 | // return the number of rows affected by last DML operation (INSERT/UPDATE/DELETE) 58 | int cMySqlRowsAffected (int pConnection); 59 | 60 | // Reads and returns the key value from standard INI-file 61 | string ReadIni (string pFileName, // INI-filename 62 | string pSection, // name of section 63 | string pKey); // name of key 64 | #import 65 | 66 | 67 | //interface variables 68 | int MySqlErrorNumber; // recent MySQL error number 69 | string MySqlErrorDescription; // error description 70 | 71 | // return the version of MySQLCursor.DLL 72 | string MySqlVersion() 73 | { 74 | return(cMySqlVersion()); 75 | } 76 | 77 | // Interface function MySqlConnect - make connection to MySQL database using parameter: 78 | // pHost - DNS name or IP-address 79 | // pUser - database user (f.e. root) 80 | // pPassword - password of user (f.e. Zok1LmVdx) 81 | // pDatabase - database name (f.e. metatrader) 82 | // pPort - TCP/IP port of database listener (f.e. 3306) 83 | // pSocket - unix socket (for sockets or named pipes using) 84 | // pClientFlag - combination of the flags for features (usual 0) 85 | // ------------------------------------------------------------------------------------ 86 | // RETURN - database connection identifier 87 | // if return value = 0, check MySqlErrorNumber and MySqlErrorDescription 88 | int MySqlConnect(string pHost, string pUser, string pPassword, string pDatabase, int pPort, string pSocket, int pClientFlag) 89 | { 90 | int connection; 91 | ClearErrors(); 92 | connection = cMySqlConnect(pHost, pUser, pPassword, pDatabase, pPort, pSocket, pClientFlag); 93 | 94 | if (SQLTrace) Print ("Connecting to Host=", pHost, ", User=", pUser, ", Database=", pDatabase, " DBID#", connection); 95 | 96 | if (connection == -1) 97 | { 98 | MySqlErrorNumber = cGetMySqlErrorNumber(-1); 99 | MySqlErrorDescription = cGetMySqlErrorDescription(-1); 100 | if (SQLTrace) Print ("Connection error #",MySqlErrorNumber," ",MySqlErrorDescription); 101 | } 102 | else 103 | { 104 | MySqlLastConnect = TimeCurrent(); 105 | if (SQLTrace) Print ("Connected! DBID#",connection); 106 | } 107 | 108 | return (connection); 109 | } 110 | 111 | // Interface function MySqlDisconnect - closes connection "pConnection" to database 112 | // When no connection was established - nothing happends 113 | void MySqlDisconnect(int &pConnection) 114 | { 115 | ClearErrors(); 116 | if (pConnection != -1) 117 | { 118 | cMySqlDisconnect(pConnection); 119 | if (SQLTrace) Print ("DBID#",pConnection," disconnected"); 120 | pConnection = -1; 121 | } 122 | } 123 | 124 | // Interface function MySqlExecute - executes SQL query via specified connection 125 | // pConnection - opened database connection 126 | // pQuery - SQL query 127 | // ------------------------------------------------------ 128 | // RETURN - true : when execution succeded 129 | // - false: when any error was raised (see MySqlErrorNumber, MySqlErrorDescription, MySqlErrorQuery) 130 | bool MySqlExecute(int pConnection, string pQuery) 131 | { 132 | ClearErrors(); 133 | if (SQLTrace) {Print ("DBID#",pConnection,", CMD:",pQuery);} 134 | if (pConnection == -1) 135 | { 136 | // no connection 137 | MySqlErrorNumber = -2; 138 | MySqlErrorDescription = "No connection to the database."; 139 | if (SQLTrace) Print ("CMD>",MySqlErrorNumber, ": ", MySqlErrorDescription); 140 | return (false); 141 | } 142 | 143 | if (!cMySqlExecute(pConnection, pQuery)) 144 | { 145 | MySqlErrorNumber = cGetMySqlErrorNumber(pConnection); 146 | MySqlErrorDescription = cGetMySqlErrorDescription(pConnection); 147 | if (SQLTrace) Print ("CMD>",MySqlErrorNumber, ": ", MySqlErrorDescription); 148 | return (false); 149 | } 150 | return (true); 151 | } 152 | 153 | // creates an cursor based on SELECT statement 154 | // return valuse - cursor identifier 155 | int MySqlCursorOpen(int pConnection, string pQuery) 156 | { 157 | int result; 158 | if (SQLTrace) {Print ("DBID#",pConnection,", QRY:",pQuery);} 159 | ClearErrors(); 160 | result = cMySqlCursorOpen(pConnection, pQuery); 161 | if (result == -1) 162 | { 163 | MySqlErrorNumber = cGetMySqlErrorNumber(pConnection); 164 | MySqlErrorDescription = cGetMySqlErrorDescription(pConnection); 165 | if (SQLTrace) Print ("QRY>",MySqlErrorNumber, ": ", MySqlErrorDescription); 166 | } 167 | return (result); 168 | } 169 | 170 | // closes opened cursor 171 | void MySqlCursorClose(int pCursorID) 172 | { 173 | ClearErrors(); 174 | cMySqlCursorClose(pCursorID); 175 | MySqlErrorNumber = cGetCursorErrorNumber(pCursorID); 176 | MySqlErrorDescription = cGetCursorErrorDescription(pCursorID); 177 | if (SQLTrace) 178 | { 179 | if (MySqlErrorNumber!=0) 180 | { 181 | Print ("Cursor #",pCursorID," closing error:", MySqlErrorNumber, ": ", MySqlErrorDescription); 182 | } 183 | else 184 | { 185 | Print ("Cursor #",pCursorID," closed"); 186 | } 187 | } 188 | } 189 | 190 | // return number of rows was selected by cursor 191 | int MySqlCursorRows(int pCursorID) 192 | { 193 | int result; 194 | result = cMySqlCursorRows(pCursorID); 195 | MySqlErrorNumber = cGetCursorErrorNumber(pCursorID); 196 | MySqlErrorDescription = cGetCursorErrorDescription(pCursorID); 197 | if (SQLTrace) Print ("Cursor #",pCursorID,", rows: ",result); 198 | return (result); 199 | } 200 | 201 | // fetch next row from cursor into current row buffer 202 | // return true - if succeeded, otherwise - false 203 | bool MySqlCursorFetchRow(int pCursorID) 204 | { 205 | bool result; 206 | result = cMySqlCursorFetchRow(pCursorID); 207 | MySqlErrorNumber = cGetCursorErrorNumber(pCursorID); 208 | MySqlErrorDescription = cGetCursorErrorDescription(pCursorID); 209 | return (result); 210 | } 211 | 212 | // retrieves the value from current row was fetched by cursor 213 | string MySqlGetRowField(int pCursorID, int pField) 214 | { 215 | string result; 216 | result = cMySqlGetRowField(pCursorID, pField); 217 | MySqlErrorNumber = cGetCursorErrorNumber(pCursorID); 218 | MySqlErrorDescription = cGetCursorErrorDescription(pCursorID); 219 | return (result); 220 | } 221 | 222 | double MySqlGetRowFieldDD(int pCursorID, int pField) 223 | { 224 | double result; 225 | result = cMySqlGetRowField(pCursorID, pField); 226 | MySqlErrorNumber = cGetCursorErrorNumber(pCursorID); 227 | MySqlErrorDescription = cGetCursorErrorDescription(pCursorID); 228 | return (result); 229 | } 230 | 231 | int MySqlGetFieldAsInt(int pCursorID, int pField) 232 | { 233 | return (StrToInteger(MySqlGetRowField(pCursorID, pField))); 234 | } 235 | 236 | double MySqlGetFieldAsDouble(int pCursorID, int pField) 237 | { 238 | return (StrToDouble(MySqlGetRowField(pCursorID, pField))); 239 | } 240 | 241 | double MySqlGetFieldAsDoubleDD(int pCursorID, int pField) 242 | { 243 | return (MySqlGetRowFieldDD(pCursorID, pField)); 244 | } 245 | 246 | datetime MySqlGetFieldAsDatetime(int pCursorID, int pField) 247 | { 248 | string x = MySqlGetRowField(pCursorID, pField); 249 | StringReplace(x,"-","."); 250 | return (StringToTime(x)); 251 | } 252 | 253 | string MySqlGetFieldAsString(int pCursorID, int pField) 254 | { 255 | return (MySqlGetRowField(pCursorID, pField)); 256 | } 257 | 258 | // just to clear error buffer before any function started its functionality 259 | void ClearErrors() 260 | { 261 | MySqlErrorNumber = 0; 262 | MySqlErrorDescription = "No errors."; 263 | } 264 | 265 | int MySqlRowsAffected (int pConnection) 266 | { 267 | return (cMySqlRowsAffected(pConnection)); 268 | } 269 | 270 | 271 | /******************************************************************** 272 | * MySQL standard definitions * 273 | ********************************************************************/ 274 | #define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */ 275 | #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ 276 | #define CLIENT_LONG_FLAG 4 /* Get all column flags */ 277 | #define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */ 278 | #define CLIENT_NO_SCHEMA 16 /* Don't allow database.table.column */ 279 | #define CLIENT_COMPRESS 32 /* Can use compression protocol */ 280 | #define CLIENT_ODBC 64 /* Odbc client */ 281 | #define CLIENT_LOCAL_FILES 128 /* Can use LOAD DATA LOCAL */ 282 | #define CLIENT_IGNORE_SPACE 256 /* Ignore spaces before '(' */ 283 | #define CLIENT_PROTOCOL_41 512 /* New 4.1 protocol */ 284 | #define CLIENT_INTERACTIVE 1024 /* This is an interactive client */ 285 | #define CLIENT_SSL 2048 /* Switch to SSL after handshake */ 286 | #define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */ 287 | #define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */ 288 | #define CLIENT_RESERVED 16384 /* Old flag for 4.1 protocol */ 289 | #define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */ 290 | #define CLIENT_MULTI_STATEMENTS 65536 /* Enable/disable multi-stmt support */ 291 | #define CLIENT_MULTI_RESULTS 131072 /* Enable/disable multi-results */ 292 | #define CLIENT_PS_MULTI_RESULTS 262144 /* Multi-results in PS-protocol */ 293 | 294 | -------------------------------------------------------------------------------- /MQL/Include/Symbols.mqh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TalaikisInc/data_server/2e00fbc32b6770a90c4ed7b0d7b78e7b5c98c215/MQL/Include/Symbols.mqh -------------------------------------------------------------------------------- /MQL/Indicators/z4_DATA_MODEL_to_CSV_1.0.mq4: -------------------------------------------------------------------------------- 1 | /* 2 | * Developed by Quantrade Ltd. 3 | * QUANTRADE.CO.UK 4 | * Copyright 2016 Quantrade Ltd. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 9 | */ 10 | 11 | #property version "1.0" 12 | #property indicator_chart_window 13 | 14 | extern int divider = 3; 15 | extern int start = 0; 16 | extern int end = 1; 17 | 18 | #include 19 | 20 | static string sSymbols[100]; 21 | static int iSymbols; 22 | static datetime tPreviousTime; 23 | int s; 24 | int i; 25 | string sPeriod = "," + PeriodToStr(); 26 | string open, high, low, close, volume, date_time; 27 | 28 | //+------------------------------------------------------------------+ 29 | //| Custom indicator initialization function | 30 | //+------------------------------------------------------------------+ 31 | 32 | int init() 33 | { 34 | //---- indicators 35 | 36 | //---- 37 | return(0); 38 | } 39 | //+------------------------------------------------------------------+ 40 | //| Custom indicator deinitialization function | 41 | //+------------------------------------------------------------------+ 42 | int deinit() 43 | { 44 | //---- 45 | 46 | //---- 47 | return(0); 48 | } 49 | //+------------------------------------------------------------------+ 50 | //| Custom indicator iteration function | 51 | //+------------------------------------------------------------------+ 52 | int start() 53 | { 54 | int bars = IndicatorCounted() - 1; 55 | 56 | // only load the Symbols once into the array "sSymbols" 57 | if (iSymbols == 0) 58 | iSymbols = Symbols(sSymbols) / divider; 59 | 60 | if (Refresh() == true) 61 | { 62 | DoExport(); 63 | } 64 | 65 | //---- 66 | return(0); 67 | } 68 | //+------------------------------------------------------------------+ 69 | 70 | //update base only once a bar 71 | bool Refresh() 72 | { 73 | static datetime PrevBar; 74 | 75 | if (PrevBar != iTime(NULL, Period(), 0)) 76 | { 77 | PrevBar = iTime(NULL, Period(), 0); 78 | return(true); 79 | } 80 | else 81 | { 82 | return(false); 83 | } 84 | } 85 | 86 | string make_symbol(string text) 87 | { 88 | int final_; 89 | final_ = StringReplace(text, "_", ""); 90 | final_ += StringReplace(text, "#", ""); 91 | final_ += StringReplace(text, ".", ""); 92 | final_ += StringReplace(text, "&", ""); 93 | final_ += StringReplace(text, "-", ""); 94 | 95 | return(text); 96 | } 97 | 98 | void DoExport() 99 | { 100 | int filehandle; 101 | 102 | for (s = start * iSymbols; s <= iSymbols * end; s++) 103 | { 104 | string symbol = make_symbol(sSymbols[s]); 105 | string company = AccountCompany(); 106 | int period; 107 | 108 | for (int p = 0; p <= 2; p++) 109 | { 110 | if (p == 0) 111 | { 112 | int day = 1440; 113 | 114 | if (StringLen(symbol) > 0 && StringLen(company)) 115 | { 116 | filehandle = FileOpen("DATA_MODEL_" + company + "_" + symbol + "_1440.csv", FILE_WRITE | FILE_CSV); 117 | } 118 | else 119 | { 120 | filehandle = -999; 121 | } 122 | 123 | for (int i = 0; i <= iBars(sSymbols[s], day) - 1; i++) 124 | { 125 | date_time = TimeToStr(iTime(sSymbols[s], day, i), TIME_DATE | TIME_MINUTES | TIME_SECONDS); 126 | open = DoubleToStr(iOpen(sSymbols[s], day, i), 5); 127 | high = DoubleToStr(iHigh(sSymbols[s], day, i), 5); 128 | low = DoubleToStr(iLow(sSymbols[s], day, i), 5); 129 | close = DoubleToStr(iClose(sSymbols[s], day, i), 5); 130 | volume = IntegerToString(iVolume(sSymbols[s], day, i)); 131 | string buffer; 132 | 133 | if (i == 0) 134 | { 135 | if (filehandle != -999) 136 | { 137 | buffer = "DATE_TIME,OPEN,HIGH,LOW,CLOSE, VOLUME"; 138 | FileWrite(filehandle, buffer); 139 | 140 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 141 | FileWrite(filehandle, buffer); 142 | } 143 | } 144 | else 145 | { 146 | if (filehandle != -999) 147 | { 148 | if (close != 0 && open != 0 && high != 0 && low != 0) 149 | { 150 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 151 | 152 | FileWrite(filehandle, buffer); 153 | } 154 | } 155 | } 156 | } 157 | if (filehandle != INVALID_HANDLE) 158 | { 159 | FileClose(filehandle); 160 | Print("Created file for " + symbol); 161 | } 162 | else 163 | { 164 | Print("Error in FileOpen. Error code=", GetLastError()); 165 | } 166 | } 167 | 168 | if (p == 1) 169 | { 170 | int week = 43200; 171 | 172 | if (StringLen(symbol) > 0 && StringLen(company)) 173 | { 174 | filehandle = FileOpen("DATA_MODEL_" + company + "_" + symbol + "_43200.csv", FILE_WRITE | FILE_CSV); 175 | } 176 | else 177 | { 178 | filehandle = -999; 179 | } 180 | 181 | for (i = 0; i <= iBars(sSymbols[s], week) - 1; i++) 182 | { 183 | date_time = TimeToStr(iTime(sSymbols[s], week, i), TIME_DATE | TIME_MINUTES | TIME_SECONDS); 184 | open = DoubleToStr(iOpen(sSymbols[s], week, i), 5); 185 | high = DoubleToStr(iHigh(sSymbols[s], week, i), 5); 186 | low = DoubleToStr(iLow(sSymbols[s], week, i), 5); 187 | close = DoubleToStr(iClose(sSymbols[s], week, i), 5); 188 | volume = IntegerToString(iVolume(sSymbols[s], week, i)); 189 | buffer = ""; 190 | 191 | if (i == 0) 192 | { 193 | if (filehandle != -999) 194 | { 195 | buffer = "DATE_TIME,OPEN,HIGH,LOW,CLOSE, VOLUME"; 196 | FileWrite(filehandle, buffer); 197 | 198 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 199 | FileWrite(filehandle, buffer); 200 | } 201 | } 202 | else 203 | { 204 | if (filehandle != -999) 205 | { 206 | if (close != 0 && open != 0 && high != 0 && low != 0) 207 | { 208 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 209 | 210 | FileWrite(filehandle, buffer); 211 | } 212 | } 213 | } 214 | } 215 | if (filehandle != INVALID_HANDLE) 216 | { 217 | FileClose(filehandle); 218 | Print("Created file for " + symbol); 219 | } 220 | else 221 | { 222 | Print("Error in FileOpen. Error code=", GetLastError()); 223 | } 224 | } 225 | 226 | if (p == 2) 227 | { 228 | int month = 10080; 229 | 230 | if (StringLen(symbol) > 0 && StringLen(company)) 231 | { 232 | filehandle = FileOpen("DATA_MODEL_" + company + "_" + symbol + "_10080.csv", FILE_WRITE | FILE_CSV); 233 | } 234 | else 235 | { 236 | filehandle = -999; 237 | } 238 | 239 | for (i = 0; i <= iBars(sSymbols[s], month) - 1; i++) 240 | { 241 | date_time = TimeToStr(iTime(sSymbols[s], month, i), TIME_DATE | TIME_MINUTES | TIME_SECONDS); 242 | open = DoubleToStr(iOpen(sSymbols[s], month, i), 5); 243 | high = DoubleToStr(iHigh(sSymbols[s], month, i), 5); 244 | low = DoubleToStr(iLow(sSymbols[s], month, i), 5); 245 | close = DoubleToStr(iClose(sSymbols[s], month, i), 5); 246 | volume = IntegerToString(iVolume(sSymbols[s], month, i)); 247 | buffer = ""; 248 | 249 | if (i == 0) 250 | { 251 | if (filehandle != -999) 252 | { 253 | buffer = "DATE_TIME,OPEN,HIGH,LOW,CLOSE, VOLUME"; 254 | FileWrite(filehandle, buffer); 255 | 256 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 257 | FileWrite(filehandle, buffer); 258 | } 259 | } 260 | else 261 | { 262 | if (filehandle != -999) 263 | { 264 | if (close != 0 && open != 0 && high != 0 && low != 0) 265 | { 266 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 267 | 268 | FileWrite(filehandle, buffer); 269 | } 270 | } 271 | } 272 | } 273 | if (filehandle != INVALID_HANDLE) 274 | { 275 | FileClose(filehandle); 276 | Print("Created file for " + symbol); 277 | } 278 | else 279 | { 280 | Print("Error in FileOpen. Error code=", GetLastError()); 281 | } 282 | } 283 | } //for periods 284 | } // for symbols 285 | } 286 | -------------------------------------------------------------------------------- /MQL/Indicators/z4_DATA_MODEL_to_CSV_Single_1.0.mq4: -------------------------------------------------------------------------------- 1 | /* 2 | * Developed by Quantrade Ltd. 3 | * QUANTRADE.CO.UK 4 | * Copyright 2016 Quantrade Ltd. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 9 | */ 10 | 11 | #property version "1.0" 12 | #property indicator_chart_window 13 | 14 | int s; 15 | int i; 16 | string open, high, low, close, volume, date_time; 17 | 18 | //+------------------------------------------------------------------+ 19 | //| Custom indicator initialization function | 20 | //+------------------------------------------------------------------+ 21 | 22 | int init() 23 | { 24 | //---- indicators 25 | 26 | //---- 27 | return(0); 28 | } 29 | //+------------------------------------------------------------------+ 30 | //| Custom indicator deinitialization function | 31 | //+------------------------------------------------------------------+ 32 | int deinit() 33 | { 34 | //---- 35 | 36 | //---- 37 | return(0); 38 | } 39 | //+------------------------------------------------------------------+ 40 | //| Custom indicator iteration function | 41 | //+------------------------------------------------------------------+ 42 | int start() 43 | { 44 | int bars = IndicatorCounted() - 1; 45 | 46 | if (Refresh() == true) 47 | { 48 | DoExport(); 49 | } 50 | 51 | //---- 52 | return(0); 53 | } 54 | //+------------------------------------------------------------------+ 55 | 56 | //update base only once a bar 57 | bool Refresh() 58 | { 59 | static datetime PrevBar; 60 | 61 | if (PrevBar != iTime(NULL, Period(), 0)) 62 | { 63 | PrevBar = iTime(NULL, Period(), 0); 64 | return(true); 65 | } 66 | else 67 | { 68 | return(false); 69 | } 70 | } 71 | 72 | string make_symbol(string text) 73 | { 74 | int final_; 75 | final_ = StringReplace(text, "_", ""); 76 | final_ += StringReplace(text, "#", ""); 77 | final_ += StringReplace(text, ".", ""); 78 | final_ += StringReplace(text, "&", ""); 79 | final_ += StringReplace(text, "-", ""); 80 | 81 | return(text); 82 | } 83 | 84 | void DoExport() 85 | { 86 | int filehandle; 87 | 88 | string symbol = make_symbol(Symbol()); 89 | string company = AccountCompany(); 90 | int period; 91 | 92 | int day = 1440; 93 | 94 | if (StringLen(symbol) > 0 && StringLen(company)) 95 | { 96 | filehandle = FileOpen("DATA_MODEL_" + company + "_" + symbol + "_1440.csv", FILE_WRITE | FILE_CSV); 97 | } 98 | else 99 | { 100 | filehandle = -999; 101 | } 102 | 103 | for (int i = 0; i <= iBars(Symbol(), day) - 1; i++) 104 | { 105 | date_time = TimeToStr(iTime(Symbol(), day, i), TIME_DATE | TIME_MINUTES | TIME_SECONDS); 106 | open = DoubleToStr(iOpen(Symbol(), day, i), 5); 107 | high = DoubleToStr(iHigh(Symbol(), day, i), 5); 108 | low = DoubleToStr(iLow(Symbol(), day, i), 5); 109 | close = DoubleToStr(iClose(Symbol(), day, i), 5); 110 | volume = IntegerToString(iVolume(Symbol(), day, i)); 111 | string buffer; 112 | 113 | if (i == 0) 114 | { 115 | if (filehandle != -999) 116 | { 117 | buffer = "DATE_TIME,OPEN,HIGH,LOW,CLOSE, VOLUME"; 118 | FileWrite(filehandle, buffer); 119 | 120 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 121 | FileWrite(filehandle, buffer); 122 | } 123 | } 124 | else 125 | { 126 | if (filehandle != -999) 127 | { 128 | if (close != 0 && open != 0 && high != 0 && low != 0) 129 | { 130 | buffer = date_time + "," + open + "," + high + "," + low + "," + close + "," + volume; 131 | 132 | FileWrite(filehandle, buffer); 133 | } 134 | } 135 | } 136 | } 137 | if (filehandle != INVALID_HANDLE) 138 | { 139 | FileClose(filehandle); 140 | Print("Created file for " + symbol); 141 | } 142 | else 143 | { 144 | Print("Error in FileOpen. Error code=", GetLastError()); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /MQL/Indicators/z4_DATA_MODEL_to_MySQL_1.0.mq4: -------------------------------------------------------------------------------- 1 | /* 2 | * Developed by Quantrade Ltd. 3 | * QUANTRADE.CO.UK 4 | * Copyright 2016 Quantrade Ltd. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 9 | */ 10 | 11 | #property version "1.0" 12 | #property indicator_chart_window 13 | 14 | //extern string host = ""; 15 | // dev 16 | extern string host = "localhost"; 17 | extern int port = 3306; 18 | extern int socket = 0; 19 | extern string user = "root"; 20 | //extern string password = ""; 21 | extern string password = ""; 22 | extern string dbName = "admin_quantrade"; 23 | extern int divider = 3; 24 | extern int start = 0; 25 | extern int end = 1; 26 | extern bool ParseAll = False; 27 | 28 | #include 29 | #include 30 | 31 | static string sSymbols[100]; 32 | static int iSymbols; 33 | static datetime tPreviousTime; 34 | int DB; // database identifier 35 | int s; 36 | int i; 37 | string sPeriod = "," + PeriodToStr(); 38 | 39 | //+------------------------------------------------------------------+ 40 | //| Custom indicator initialization function | 41 | //+------------------------------------------------------------------+ 42 | 43 | int init() 44 | { 45 | //---- indicators 46 | 47 | //---- 48 | return(0); 49 | } 50 | //+------------------------------------------------------------------+ 51 | //| Custom indicator deinitialization function | 52 | //+------------------------------------------------------------------+ 53 | int deinit() 54 | { 55 | //---- 56 | 57 | //---- 58 | return(0); 59 | } 60 | //+------------------------------------------------------------------+ 61 | //| Custom indicator iteration function | 62 | //+------------------------------------------------------------------+ 63 | int start() 64 | { 65 | int bars = IndicatorCounted() - 1; 66 | 67 | // only load the Symbols once into the array "sSymbols" 68 | if (iSymbols == 0) 69 | iSymbols = Symbols(sSymbols) / divider; 70 | 71 | //Print (MySqlVersion()); 72 | 73 | if (Refresh(Period()) == true) 74 | { 75 | // open database connection 76 | Print("Connecting..."); 77 | 78 | //connect to database 79 | DB = MySqlConnect(host, user, password, dbName, port, socket, CLIENT_MULTI_STATEMENTS); 80 | 81 | if (DB == -1) 82 | { 83 | Print("Connection to MySQL database failed! Error: " + MySqlErrorDescription); 84 | } 85 | else 86 | { 87 | Print("Connected! DB_ID#", DB); 88 | } 89 | DoExport(); 90 | MySqlDisconnect(DB); 91 | Print("MySQL disconnected. Bye."); 92 | } 93 | 94 | //---- 95 | return(0); 96 | } 97 | //+------------------------------------------------------------------+ 98 | 99 | //update base only once a bar 100 | bool Refresh(int _per) 101 | { 102 | static datetime PrevBar; 103 | //Print("Refresh times. PrevBar: "+PrevBar); 104 | 105 | if (PrevBar != iTime(NULL, _per, 0)) 106 | { 107 | PrevBar = iTime(NULL, _per, 0); 108 | return(true); 109 | } 110 | else 111 | { 112 | return(false); 113 | } 114 | } 115 | 116 | string make_symbol(string text) 117 | { 118 | int final; 119 | final = StringReplace(text, "_", ""); 120 | final += StringReplace(text, "#", ""); 121 | final += StringReplace(text, ".", ""); 122 | final += StringReplace(text, "&", ""); 123 | final += StringReplace(text, "-", ""); 124 | 125 | return(text); 126 | } 127 | 128 | void DoExport() 129 | { 130 | string Query; 131 | int i, Cursor, Rows; 132 | 133 | for (s = start * iSymbols; s <= iSymbols * end; s++) 134 | { 135 | int period = Period(); 136 | string to_find = make_symbol(sSymbols[s]); 137 | string broker = "AvaFx"; 138 | 139 | Query = "SELECT date_time FROM collector_data WHERE symbol='" + to_find + "' AND period=" + period + " AND broker='" + broker + "' ORDER BY date_time DESC LIMiT 1;"; 140 | 141 | Print("SQL> ", Query); 142 | 143 | Cursor = MySqlCursorOpen(DB, Query); 144 | 145 | if (Cursor >= 0) 146 | { 147 | Rows = MySqlCursorRows(Cursor); 148 | Print(Rows, " row(s) selected."); 149 | 150 | if (Rows > 0) 151 | { 152 | if (MySqlCursorFetchRow(Cursor)) 153 | { 154 | datetime date_time_fetched = MySqlGetFieldAsDatetime(Cursor, 0); 155 | Print("Last date from database:"); 156 | Print(TimeToStr(date_time_fetched, TIME_DATE | TIME_MINUTES | TIME_SECONDS)); 157 | } 158 | } 159 | else 160 | { 161 | Print("Here"); 162 | } 163 | MySqlCursorClose(Cursor); // NEVER FORGET TO CLOSE CURSOR !!! 164 | } 165 | else 166 | { 167 | Print("Cursor opening failed. Error: ", MySqlErrorDescription); 168 | MySqlCursorClose(Cursor); // NEVER FORGET TO CLOSE CURSOR !!! 169 | } 170 | 171 | Query = "START TRANSACTION;"; 172 | 173 | if (MySqlExecute(DB, Query)) 174 | { 175 | Print("Succeeded: ", Query); 176 | } 177 | else 178 | { 179 | Print("Error: ", MySqlErrorDescription, " with: ", Query); 180 | } 181 | 182 | for (i = 1; i <= iBars(sSymbols[s], 0); i++) 183 | { 184 | //for each symbol 185 | if (iTime(sSymbols[s], 0, i) >= date_time_fetched || ParseAll) 186 | { 187 | string date_time = TimeToStr(iTime(sSymbols[s], 0, i), TIME_DATE | TIME_MINUTES | TIME_SECONDS); 188 | double close = NormalizeDouble(iClose(sSymbols[s], 0, i), 5); 189 | double open = NormalizeDouble(iOpen(sSymbols[s], 0, i), 5); 190 | double high = NormalizeDouble(iHigh(sSymbols[s], 0, i), 5); 191 | double low = NormalizeDouble(iLow(sSymbols[s], 0, i), 5); 192 | int volume = iVolume(sSymbols[s], 0, i); 193 | 194 | if (StringLen(to_find) > 0) 195 | { 196 | Query = "INSERT INTO collector_data (date_time, symbol, open, high, low, close, period, volume, broker) VALUES ('" + 197 | date_time + "', '" + 198 | to_find + "', " + 199 | open + ", " + 200 | high + ", " + 201 | low + ", " + 202 | close + ", " + 203 | period + ", " + 204 | volume + ", '" + 205 | broker + "');"; 206 | 207 | 208 | if (MySqlExecute(DB, Query)) 209 | { 210 | Print("Succeeded: ", Query); 211 | } 212 | else 213 | { 214 | Print("Error: ", MySqlErrorDescription, " with: ", Query); 215 | } 216 | } 217 | } 218 | } 219 | 220 | Query = "COMMIT;"; 221 | 222 | if (MySqlExecute(DB, Query)) 223 | { 224 | Print("Succeeded: ", Query); 225 | } 226 | else 227 | { 228 | Print("Error: ", MySqlErrorDescription, " with: ", Query); 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /MQL/Indicators/z4_Symbol_Properties_to_MySQL_1.0.mq4: -------------------------------------------------------------------------------- 1 | /* 2 | * Developed by Quantrade Ltd. 3 | * QUANTRADE.CO.UK 4 | * Copyright 2016 Quantrade Ltd. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 9 | */ 10 | 11 | #property version "1.0" 12 | #property indicator_chart_window 13 | 14 | //dev 15 | //extern string host = "localhost"; 16 | //server 17 | extern string host = ""; 18 | 19 | extern int port = 3306; 20 | extern int socket = 0; 21 | extern string user = "root"; 22 | 23 | //dev 24 | //extern string password = ""; 25 | //server 26 | extern string password = ""; 27 | 28 | extern string dbName = "admin_quantrade"; 29 | 30 | #include 31 | #include 32 | 33 | static string sSymbols[100]; 34 | static int iSymbols; 35 | static datetime tPreviousTime; 36 | int DB; // database identifier 37 | int s; 38 | int i; 39 | string sPeriod = "," + PeriodToStr(); 40 | 41 | //+------------------------------------------------------------------+ 42 | //| Custom indicator initialization function | 43 | //+------------------------------------------------------------------+ 44 | 45 | int init() 46 | { 47 | //---- indicators 48 | 49 | //---- 50 | return(0); 51 | } 52 | //+------------------------------------------------------------------+ 53 | //| Custom indicator deinitialization function | 54 | //+------------------------------------------------------------------+ 55 | int deinit() 56 | { 57 | //---- 58 | 59 | //---- 60 | return(0); 61 | } 62 | //+------------------------------------------------------------------+ 63 | //| Custom indicator iteration function | 64 | //+------------------------------------------------------------------+ 65 | int start() 66 | { 67 | int bars = IndicatorCounted() - 1; 68 | 69 | // only load the Symbols once into the array "sSymbols" 70 | if (iSymbols == 0) 71 | iSymbols = Symbols(sSymbols); 72 | 73 | //Print (MySqlVersion()); 74 | 75 | if (Refresh(Period()) == true) 76 | { 77 | // open database connection 78 | Print("Connecting..."); 79 | 80 | //connect to database 81 | DB = MySqlConnect(host, user, password, dbName, port, socket, CLIENT_MULTI_STATEMENTS); 82 | 83 | if (DB == -1) 84 | { 85 | Print("Connection to MySQL database failed! Error: " + MySqlErrorDescription); 86 | } 87 | else 88 | { 89 | Print("Connected! DB_ID#", DB); 90 | } 91 | DoExport(); 92 | MySqlDisconnect(DB); 93 | Print("MySQL disconnected. Bye."); 94 | } 95 | 96 | //---- 97 | return(0); 98 | } 99 | //+------------------------------------------------------------------+ 100 | 101 | //update base only once a bar 102 | bool Refresh(int _per) 103 | { 104 | static datetime PrevBar; 105 | //Print("Refresh times. PrevBar: "+PrevBar); 106 | 107 | if (PrevBar != iTime(NULL, _per, 0)) 108 | { 109 | PrevBar = iTime(NULL, _per, 0); 110 | return(true); 111 | } 112 | else 113 | { 114 | return(false); 115 | } 116 | } 117 | 118 | string make_symbol(string text) 119 | { 120 | int final_; 121 | final_ = StringReplace(text, "_", ""); 122 | final_ += StringReplace(text, "#", ""); 123 | final_ += StringReplace(text, ".", ""); 124 | final_ += StringReplace(text, "&", ""); 125 | final_ += StringReplace(text, "-", ""); 126 | 127 | return(text); 128 | } 129 | 130 | void DoExport() 131 | { 132 | string Query; 133 | int i, Cursor, Rows; 134 | 135 | Query = "START TRANSACTION;"; 136 | 137 | if (MySqlExecute(DB, Query)) 138 | { 139 | Print("Succeeded: ", Query); 140 | } 141 | else 142 | { 143 | //Print("Error: ", MySqlErrorDescription, " with: ", Query); 144 | } 145 | 146 | for (s = 0; s <= iSymbols; s++) 147 | { 148 | //for each symbol 149 | double spread = NormalizeDouble(MarketInfo(sSymbols[s], MODE_SPREAD), 5); 150 | double tick_value = NormalizeDouble(MarketInfo(sSymbols[s], MODE_TICKVALUE), 5); 151 | double tick_size = NormalizeDouble(MarketInfo(sSymbols[s], MODE_TICKSIZE), 5); 152 | double margin_initial = NormalizeDouble(MarketInfo(sSymbols[s], MODE_MARGINREQUIRED), 2); 153 | double digits = NormalizeDouble(MarketInfo(sSymbols[s], MODE_DIGITS), 2); 154 | double profit_mode = NormalizeDouble(MarketInfo(sSymbols[s], MODE_PROFITCALCMODE), 2); 155 | double points = NormalizeDouble(MarketInfo(sSymbols[s], MODE_POINT), 2); 156 | 157 | string profit_currency = SymbolInfoString(sSymbols[s], SYMBOL_CURRENCY_PROFIT); 158 | string description = SymbolInfoString(sSymbols[s], SYMBOL_DESCRIPTION); 159 | double price_at_calc_time = iClose(sSymbols[s], 1440, 1); 160 | string to_find = make_symbol(sSymbols[s]); 161 | string company = AccountCompany(); 162 | 163 | if (StringLen(to_find) > 0 && StringLen(company) > 0) 164 | { 165 | //Print(company); 166 | //Print(to_find); 167 | 168 | Query = "UPDATE collector_symbols SET spread=" + spread + ", tick_value=" + tick_value + 169 | ", tick_size=" + tick_size + ", margin_initial=" + margin_initial + ", digits=" + digits + 170 | ", profit_calc=" + profit_mode + ", description='" + description + "', profit_currency='" + 171 | profit_currency + "', price_at_calc_time=" + price_at_calc_time + ", points=" + points + 172 | " WHERE symbol='" + to_find + "' AND broker='"+ company +"';"; 173 | 174 | if (MySqlExecute(DB, Query)) 175 | { 176 | Print("Succeeded: ", Query); 177 | } 178 | else 179 | { 180 | //Print("Error: ", MySqlErrorDescription, " with: ", Query); 181 | } 182 | } 183 | } 184 | 185 | Query = "COMMIT;"; 186 | 187 | if (MySqlExecute(DB, Query)) 188 | { 189 | Print("Succeeded: ", Query); 190 | } 191 | else 192 | { 193 | //Print("Error: ", MySqlErrorDescription, " with: ", Query); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /MQL/Libraries/MQLMySQL.def: -------------------------------------------------------------------------------- 1 | LIBRARY mqlmysql.dll 2 | EXPORTS 3 | 4 | cMySqlVersion 5 | cGetMySqlErrorNumber 6 | cGetMySqlErrorDescription 7 | cGetCursorErrorNumber 8 | cGetCursorErrorDescription 9 | cMySqlConnect 10 | cMySqlDisconnect 11 | cMySqlExecute 12 | cMySqlCursorOpen 13 | cMySqlCursorClose 14 | cMySqlCursorRows 15 | cMySqlCursorFetchRow 16 | cMySqlGetRowField 17 | cMySqlRowsAffected 18 | ReadIni -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # data_server 2 | 3 | data_server is WebDAV service for Metatrader 4. 4 | 5 | ## Who uses it 6 | 7 | * [Quantrade](https://github.com/xenu256/Quantrade) 8 | * [QuantBoard](https://github.com/xenu256/QuantBoard) 9 | 10 | ## How to use 11 | 12 | 1. Put z4_DATA_MODEL_to_CSV_1.0.mq4 and z4_Symbol_Properties_to_MySQL_1.0.mq4 into some chart. Symbol properties require database table (see below). 13 | 2. Configure and start WebDAV server. 14 | 3. Voila, your MT4 data is available through internet. 15 | 16 | ## Symbols table (for storing current sym properties) 17 | 18 | ```text 19 | CREATE TABLE IF NOT EXISTS symbols (id bigint(20) NOT NULL AUTO_INCREMENT, 20 | symbol varchar(20) NOT NULL, 21 | description varchar(120) DEFAULT NULL, 22 | spread decimal(10,5) DEFAULT NULL, 23 | tick_value decimal(10,5) DEFAULT NULL, 24 | tick_size decimal(8,5) DEFAULT NULL, 25 | margin_initial decimal(10,2) DEFAULT NULL, 26 | digits decimal(8,5) DEFAULT NULL, 27 | profit_calc int(2) DEFAULT NULL, 28 | profit_currency varchar(10) DEFAULT NULL, 29 | price_at_calc_time decimal(20,5) DEFAULT NULL, 30 | commission decimal(15,5) DEFAULT NULL, 31 | broker varchar(40) NOT NULL, 32 | points decimal(8,5) DEFAULT NULL, 33 | PRIMARY KEY (id), UNIQUE KEY symbol (symbol) ) 34 | ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; 35 | 36 | ALTER TABLE collector_symbols ADD UNIQUE (symbol, broker); 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 1) pip install -r requiremenst.txt 2 | 3 | 2) MT4 platforms should have MLQ indicators loaded on some of H1 chart. 4 | 5 | 3) To start WebDAV Windows server, get NSSM: 6 | https://nssm.cc/ 7 | 8 | for each folder: 9 | 2) nssm.exe install WebDAV_1 10 | 11 | 3) Enter path, ex.: C:\Users\Administrator\Anaconda2\Scripts\wsgidav.exe 12 | 13 | 4) Enter StartupDirectory, ex.: C:\Users\Administrator\Anaconda2\Scripts 14 | 15 | 3) Arguments, ex.: --host= --port=8010 --root="C:\Users\Administrator\AppData\Roaming\MetaQuotes\Terminal\4CEA74113F9B03A091193EB928E38709\MQL4\Files" --config=C:\inetpub\wwwroot\quantrade\webdav.conf 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | wsgidav 2 | cheroot 3 | -------------------------------------------------------------------------------- /webdav.conf.sample: -------------------------------------------------------------------------------- 1 | provider_mapping = {} 2 | user_mapping = {} 3 | 4 | def addShare(shareName, davProvider): 5 | provider_mapping[shareName] = davProvider 6 | 7 | 8 | def addUser(realmName, user, password, description, roles=[]): 9 | realmName = "/" + realmName.strip(r"\/") 10 | userDict = user_mapping.setdefault(realmName, {}).setdefault(user, {}) 11 | userDict["password"] = password 12 | userDict["description"] = description 13 | userDict["roles"] = roles 14 | 15 | 16 | addUser("", "", "", "") 17 | --------------------------------------------------------------------------------