metaapi.cloud-sdk
Version:
SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)
160 lines (159 loc) • 16.3 kB
JavaScript
;
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _async_to_generator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
import HistoryDatabase from './historyDatabase';
import LoggerManager from '../../logger';
import fs from 'fs';
import path from 'path';
let FilesystemHistoryDatabase = class FilesystemHistoryDatabase extends HistoryDatabase {
/**
* Returns history database instance
* @returns {HistoryDatabase} history database instance
*/ static getInstance() {
if (!FilesystemHistoryDatabase.instance) {
FilesystemHistoryDatabase.instance = new FilesystemHistoryDatabase();
}
return FilesystemHistoryDatabase.instance;
}
/**
* Loads history from database
* @param {string} accountId account id
* @param {string} application application name
* @return {Promise<{deals: Array<MetatraderDeal>, historyOrders: Array<MetatraderOrder>}>} full account history
*/ loadHistory(accountId, application) {
var _this = this;
return _async_to_generator(function*() {
let { dealsFile, historyOrdersFile } = yield _this._getDbLocation(accountId, application);
let deals = yield _this._readDb(accountId, dealsFile);
if (deals.length && Array.isArray(deals[0])) {
_this.clear(accountId, application);
deals = [];
}
deals.forEach((deal)=>deal.time = new Date(deal.time));
let historyOrders = yield _this._readDb(accountId, historyOrdersFile);
if (historyOrders.length && Array.isArray(historyOrders[0])) {
_this.clear(accountId, application);
historyOrders = [];
}
historyOrders.forEach((historyOrder)=>{
historyOrder.time = new Date(historyOrder.time);
historyOrder.doneTime = new Date(historyOrder.doneTime);
});
return {
deals,
historyOrders
};
})();
}
/**
* Removes history from database
* @param {string} accountId account id
* @param {string} application application name
* @return {Promise} promise resolving when the history is removed
*/ clear(accountId, application) {
var _this = this;
return _async_to_generator(function*() {
let { dealsFile, historyOrdersFile } = yield _this._getDbLocation(accountId, application);
if (fs.existsSync(dealsFile)) {
yield fs.promises.unlink(dealsFile);
}
if (fs.existsSync(historyOrdersFile)) {
yield fs.promises.unlink(historyOrdersFile);
}
})();
}
/**
* Flushes the new history to db
* @param {string} accountId account id
* @param {string} application application name
* @param {Array<MetatraderOrder>} newHistoryOrders history orders to save to db
* @param {Array<MetatraderDeal>} newDeals deals to save to db
* @return {Promise} promise resolving when the history is flushed
*/ flush(accountId, application, newHistoryOrders, newDeals) {
var _this = this;
return _async_to_generator(function*() {
let { dealsFile, historyOrdersFile } = yield _this._getDbLocation(accountId, application);
yield _this._appendDb(historyOrdersFile, newHistoryOrders);
yield _this._appendDb(dealsFile, newDeals);
})();
}
_getDbLocation(accountId, application) {
return _async_to_generator(function*() {
let dir = path.join(process.cwd(), '.metaapi');
yield fs.promises.mkdir(dir, {
recursive: true
});
return {
dealsFile: path.join(dir, `${accountId}-${application}-deals.bin`),
historyOrdersFile: path.join(dir, `${accountId}-${application}-historyOrders.bin`)
};
})();
}
_readDb(accountId, file) {
var _this = this;
return _async_to_generator(function*() {
if (!fs.existsSync(file)) {
return [];
}
try {
let data = yield fs.promises.readFile(file, 'utf-8');
let lines = data.split('\n');
let result = [];
for (let line of lines){
if (line.length) {
result.push(JSON.parse(line));
}
}
return result;
} catch (err) {
_this._logger.warn(`${accountId}: failed to read history db, will remove ${file} now`, err);
yield fs.promises.unlink(file);
return [];
}
})();
}
_appendDb(file, records) {
return _async_to_generator(function*() {
if (records && records.length) {
yield fs.promises.appendFile(file, records.map((r)=>JSON.stringify(r) + '\n').join(''), 'utf-8');
}
})();
}
/**
* Constructs the class instance
*/ constructor(){
super();
this._logger = LoggerManager.getLogger('FilesystemHistoryDatabase');
}
};
/**
* Provides access to history database stored on filesystem
*/ export { FilesystemHistoryDatabase as default };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBIaXN0b3J5RGF0YWJhc2UgZnJvbSAnLi9oaXN0b3J5RGF0YWJhc2UnO1xuaW1wb3J0IExvZ2dlck1hbmFnZXIgZnJvbSAnLi4vLi4vbG9nZ2VyJztcbmltcG9ydCBmcyBmcm9tICdmcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcblxuLyoqXG4gKiBQcm92aWRlcyBhY2Nlc3MgdG8gaGlzdG9yeSBkYXRhYmFzZSBzdG9yZWQgb24gZmlsZXN5c3RlbVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBGaWxlc3lzdGVtSGlzdG9yeURhdGFiYXNlIGV4dGVuZHMgSGlzdG9yeURhdGFiYXNlIHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0aGUgY2xhc3MgaW5zdGFuY2VcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5fbG9nZ2VyID0gTG9nZ2VyTWFuYWdlci5nZXRMb2dnZXIoJ0ZpbGVzeXN0ZW1IaXN0b3J5RGF0YWJhc2UnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGhpc3RvcnkgZGF0YWJhc2UgaW5zdGFuY2VcbiAgICogQHJldHVybnMge0hpc3RvcnlEYXRhYmFzZX0gaGlzdG9yeSBkYXRhYmFzZSBpbnN0YW5jZVxuICAgKi9cbiAgc3RhdGljIGdldEluc3RhbmNlKCkge1xuICAgIGlmICghRmlsZXN5c3RlbUhpc3RvcnlEYXRhYmFzZS5pbnN0YW5jZSkge1xuICAgICAgRmlsZXN5c3RlbUhpc3RvcnlEYXRhYmFzZS5pbnN0YW5jZSA9IG5ldyBGaWxlc3lzdGVtSGlzdG9yeURhdGFiYXNlKCk7XG4gICAgfVxuICAgIHJldHVybiBGaWxlc3lzdGVtSGlzdG9yeURhdGFiYXNlLmluc3RhbmNlO1xuICB9XG5cbiAgLyoqXG4gICAqIExvYWRzIGhpc3RvcnkgZnJvbSBkYXRhYmFzZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFwcGxpY2F0aW9uIGFwcGxpY2F0aW9uIG5hbWVcbiAgICogQHJldHVybiB7UHJvbWlzZTx7ZGVhbHM6IEFycmF5PE1ldGF0cmFkZXJEZWFsPiwgaGlzdG9yeU9yZGVyczogQXJyYXk8TWV0YXRyYWRlck9yZGVyPn0+fSBmdWxsIGFjY291bnQgaGlzdG9yeVxuICAgKi9cbiAgYXN5bmMgbG9hZEhpc3RvcnkoYWNjb3VudElkLCBhcHBsaWNhdGlvbikge1xuICAgIGxldCB7ZGVhbHNGaWxlLCBoaXN0b3J5T3JkZXJzRmlsZX0gPSBhd2FpdCB0aGlzLl9nZXREYkxvY2F0aW9uKGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGxldCBkZWFscyA9IGF3YWl0IHRoaXMuX3JlYWREYihhY2NvdW50SWQsIGRlYWxzRmlsZSk7XG4gICAgaWYoZGVhbHMubGVuZ3RoICYmIEFycmF5LmlzQXJyYXkoZGVhbHNbMF0pKSB7XG4gICAgICB0aGlzLmNsZWFyKGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgICAgZGVhbHMgPSBbXTtcbiAgICB9XG4gICAgZGVhbHMuZm9yRWFjaChkZWFsID0+IGRlYWwudGltZSA9IG5ldyBEYXRlKGRlYWwudGltZSkpO1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gYXdhaXQgdGhpcy5fcmVhZERiKGFjY291bnRJZCwgaGlzdG9yeU9yZGVyc0ZpbGUpO1xuICAgIGlmKGhpc3RvcnlPcmRlcnMubGVuZ3RoICYmIEFycmF5LmlzQXJyYXkoaGlzdG9yeU9yZGVyc1swXSkpIHtcbiAgICAgIHRoaXMuY2xlYXIoYWNjb3VudElkLCBhcHBsaWNhdGlvbik7XG4gICAgICBoaXN0b3J5T3JkZXJzID0gW107XG4gICAgfVxuICAgIGhpc3RvcnlPcmRlcnMuZm9yRWFjaChoaXN0b3J5T3JkZXIgPT4ge1xuICAgICAgaGlzdG9yeU9yZGVyLnRpbWUgPSBuZXcgRGF0ZShoaXN0b3J5T3JkZXIudGltZSk7XG4gICAgICBoaXN0b3J5T3JkZXIuZG9uZVRpbWUgPSBuZXcgRGF0ZShoaXN0b3J5T3JkZXIuZG9uZVRpbWUpO1xuICAgIH0pO1xuICAgIHJldHVybiB7ZGVhbHMsIGhpc3RvcnlPcmRlcnN9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgaGlzdG9yeSBmcm9tIGRhdGFiYXNlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXBwbGljYXRpb24gYXBwbGljYXRpb24gbmFtZVxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIHRoZSBoaXN0b3J5IGlzIHJlbW92ZWRcbiAgICovXG4gIGFzeW5jIGNsZWFyKGFjY291bnRJZCwgYXBwbGljYXRpb24pIHtcbiAgICBsZXQge2RlYWxzRmlsZSwgaGlzdG9yeU9yZGVyc0ZpbGV9ID0gYXdhaXQgdGhpcy5fZ2V0RGJMb2NhdGlvbihhY2NvdW50SWQsIGFwcGxpY2F0aW9uKTtcbiAgICBpZihmcy5leGlzdHNTeW5jKGRlYWxzRmlsZSkpIHtcbiAgICAgIGF3YWl0IGZzLnByb21pc2VzLnVubGluayhkZWFsc0ZpbGUpO1xuICAgIH1cbiAgICBpZihmcy5leGlzdHNTeW5jKGhpc3RvcnlPcmRlcnNGaWxlKSkge1xuICAgICAgYXdhaXQgZnMucHJvbWlzZXMudW5saW5rKGhpc3RvcnlPcmRlcnNGaWxlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRmx1c2hlcyB0aGUgbmV3IGhpc3RvcnkgdG8gZGJcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcHBsaWNhdGlvbiBhcHBsaWNhdGlvbiBuYW1lXG4gICAqIEBwYXJhbSB7QXJyYXk8TWV0YXRyYWRlck9yZGVyPn0gbmV3SGlzdG9yeU9yZGVycyBoaXN0b3J5IG9yZGVycyB0byBzYXZlIHRvIGRiXG4gICAqIEBwYXJhbSB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBuZXdEZWFscyBkZWFscyB0byBzYXZlIHRvIGRiXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gdGhlIGhpc3RvcnkgaXMgZmx1c2hlZFxuICAgKi9cbiAgYXN5bmMgZmx1c2goYWNjb3VudElkLCBhcHBsaWNhdGlvbiwgbmV3SGlzdG9yeU9yZGVycywgbmV3RGVhbHMpIHtcbiAgICBsZXQge2RlYWxzRmlsZSwgaGlzdG9yeU9yZGVyc0ZpbGV9ID0gYXdhaXQgdGhpcy5fZ2V0RGJMb2NhdGlvbihhY2NvdW50SWQsIGFwcGxpY2F0aW9uKTtcbiAgICBhd2FpdCB0aGlzLl9hcHBlbmREYihoaXN0b3J5T3JkZXJzRmlsZSwgbmV3SGlzdG9yeU9yZGVycyk7XG4gICAgYXdhaXQgdGhpcy5fYXBwZW5kRGIoZGVhbHNGaWxlLCBuZXdEZWFscyk7XG4gIH1cblxuICBhc3luYyBfZ2V0RGJMb2NhdGlvbihhY2NvdW50SWQsIGFwcGxpY2F0aW9uKSB7XG4gICAgbGV0IGRpciA9IHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCAnLm1ldGFhcGknKTtcbiAgICBhd2FpdCBmcy5wcm9taXNlcy5ta2RpcihkaXIsIHtyZWN1cnNpdmU6IHRydWV9KTtcbiAgICByZXR1cm4ge1xuICAgICAgZGVhbHNGaWxlOiBwYXRoLmpvaW4oZGlyLCBgJHthY2NvdW50SWR9LSR7YXBwbGljYXRpb259LWRlYWxzLmJpbmApLFxuICAgICAgaGlzdG9yeU9yZGVyc0ZpbGU6IHBhdGguam9pbihkaXIsIGAke2FjY291bnRJZH0tJHthcHBsaWNhdGlvbn0taGlzdG9yeU9yZGVycy5iaW5gKVxuICAgIH07XG4gIH1cblxuICBhc3luYyBfcmVhZERiKGFjY291bnRJZCwgZmlsZSkge1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhmaWxlKSkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgbGV0IGRhdGEgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkRmlsZShmaWxlLCAndXRmLTgnKTtcbiAgICAgIGxldCBsaW5lcyA9IGRhdGEuc3BsaXQoJ1xcbicpO1xuICAgICAgbGV0IHJlc3VsdCA9IFtdO1xuICAgICAgZm9yIChsZXQgbGluZSBvZiBsaW5lcykge1xuICAgICAgICBpZiAobGluZS5sZW5ndGgpIHtcbiAgICAgICAgICByZXN1bHQucHVzaChKU09OLnBhcnNlKGxpbmUpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMuX2xvZ2dlci53YXJuKGAke2FjY291bnRJZH06IGZhaWxlZCB0byByZWFkIGhpc3RvcnkgZGIsIHdpbGwgcmVtb3ZlICR7ZmlsZX0gbm93YCwgZXJyKTtcbiAgICAgIGF3YWl0IGZzLnByb21pc2VzLnVubGluayhmaWxlKTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBfYXBwZW5kRGIoZmlsZSwgcmVjb3Jkcykge1xuICAgIGlmIChyZWNvcmRzICYmIHJlY29yZHMubGVuZ3RoKSB7XG4gICAgICBhd2FpdCBmcy5wcm9taXNlcy5hcHBlbmRGaWxlKGZpbGUsIHJlY29yZHMubWFwKHIgPT4gSlNPTi5zdHJpbmdpZnkocikgKyAnXFxuJykuam9pbignJyksICd1dGYtOCcpO1xuICAgIH1cbiAgfVxuXG59XG4iXSwibmFtZXMiOlsiSGlzdG9yeURhdGFiYXNlIiwiTG9nZ2VyTWFuYWdlciIsImZzIiwicGF0aCIsIkZpbGVzeXN0ZW1IaXN0b3J5RGF0YWJhc2UiLCJnZXRJbnN0YW5jZSIsImluc3RhbmNlIiwibG9hZEhpc3RvcnkiLCJhY2NvdW50SWQiLCJhcHBsaWNhdGlvbiIsImRlYWxzRmlsZSIsImhpc3RvcnlPcmRlcnNGaWxlIiwiX2dldERiTG9jYXRpb24iLCJkZWFscyIsIl9yZWFkRGIiLCJsZW5ndGgiLCJBcnJheSIsImlzQXJyYXkiLCJjbGVhciIsImZvckVhY2giLCJkZWFsIiwidGltZSIsIkRhdGUiLCJoaXN0b3J5T3JkZXJzIiwiaGlzdG9yeU9yZGVyIiwiZG9uZVRpbWUiLCJleGlzdHNTeW5jIiwicHJvbWlzZXMiLCJ1bmxpbmsiLCJmbHVzaCIsIm5ld0hpc3RvcnlPcmRlcnMiLCJuZXdEZWFscyIsIl9hcHBlbmREYiIsImRpciIsImpvaW4iLCJwcm9jZXNzIiwiY3dkIiwibWtkaXIiLCJyZWN1cnNpdmUiLCJmaWxlIiwiZGF0YSIsInJlYWRGaWxlIiwibGluZXMiLCJzcGxpdCIsInJlc3VsdCIsImxpbmUiLCJwdXNoIiwiSlNPTiIsInBhcnNlIiwiZXJyIiwiX2xvZ2dlciIsIndhcm4iLCJyZWNvcmRzIiwiYXBwZW5kRmlsZSIsIm1hcCIsInIiLCJzdHJpbmdpZnkiLCJjb25zdHJ1Y3RvciIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLE9BQU9BLHFCQUFxQixvQkFBb0I7QUFDaEQsT0FBT0MsbUJBQW1CLGVBQWU7QUFDekMsT0FBT0MsUUFBUSxLQUFLO0FBQ3BCLE9BQU9DLFVBQVUsT0FBTztBQUtULElBQUEsQUFBTUMsNEJBQU4sTUFBTUEsa0NBQWtDSjtJQVVyRDs7O0dBR0MsR0FDRCxPQUFPSyxjQUFjO1FBQ25CLElBQUksQ0FBQ0QsMEJBQTBCRSxRQUFRLEVBQUU7WUFDdkNGLDBCQUEwQkUsUUFBUSxHQUFHLElBQUlGO1FBQzNDO1FBQ0EsT0FBT0EsMEJBQTBCRSxRQUFRO0lBQzNDO0lBRUE7Ozs7O0dBS0MsR0FDRCxBQUFNQyxZQUFZQyxTQUFTLEVBQUVDLFdBQVc7O2VBQXhDLG9CQUFBO1lBQ0UsSUFBSSxFQUFDQyxTQUFTLEVBQUVDLGlCQUFpQixFQUFDLEdBQUcsTUFBTSxNQUFLQyxjQUFjLENBQUNKLFdBQVdDO1lBQzFFLElBQUlJLFFBQVEsTUFBTSxNQUFLQyxPQUFPLENBQUNOLFdBQVdFO1lBQzFDLElBQUdHLE1BQU1FLE1BQU0sSUFBSUMsTUFBTUMsT0FBTyxDQUFDSixLQUFLLENBQUMsRUFB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