diff --git a/README.md b/README.md index 8b14a5b..42b92f3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # CrypFinder Bot -## Version 1.5 +## Version 1.55 ## Summary: CrypFinder is a Coinbase Pro API trading bot that currently implements a basic momentum trading strategy and reverse momentum trading strategy in NodeJS using the Coinbase Pro API, as well as its own custom library for the endpoints that are not supported by the now deprecated Coinbase Pro NodeJS Library. Currently, Coinbase Pro limits the number of portfolios to five, this means that the bot can run up to four trading instances simultaneously per Coinbase Pro account. This bot can be modified to trade any product pairs available on Coinbase Pro, such as BTC-USD, ETH-USD, etc., but stablecoin (USDC to other coins) and crypto markets (coin to other coins) aren't currently tested, only USD markets (USD to coins). diff --git a/strategies/momentumTrading/buyAndSell.js b/buyAndSell.js similarity index 100% rename from strategies/momentumTrading/buyAndSell.js rename to buyAndSell.js diff --git a/package-lock.json b/package-lock.json index 29d192e..3db298b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,18 +14,18 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", "dev": true }, "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -40,13 +40,19 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true } } }, "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", + "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -244,16 +250,6 @@ "concat-map": "0.0.1" } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -425,9 +421,9 @@ } }, "dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" }, "ecc-jsbn": { "version": "0.1.2", @@ -454,31 +450,33 @@ } }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "eslint": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", - "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz", + "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.1", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", @@ -490,7 +488,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -499,7 +497,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" } @@ -532,9 +530,9 @@ } }, "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, "espree": { @@ -700,29 +698,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -732,9 +713,9 @@ } }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -785,27 +766,12 @@ "har-schema": "^2.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -853,15 +819,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -883,18 +840,6 @@ "is-extglob": "^2.1.1" } }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -974,22 +919,16 @@ "type-check": "~0.4.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lodash.truncate": { @@ -1377,26 +1316,23 @@ } }, "table": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.3.2.tgz", - "integrity": "sha512-I9/Ca6Huf2oxFag7crD0DhA+arIdfLtWunSn0NIXSzjtUlDgIBGVZY7SsMkNPNT3Psd/z4gza0nuEpmra9eRbg==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "ajv": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", - "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.5.0.tgz", + "integrity": "sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", diff --git a/package.json b/package.json index 749c2b2..bfa3350 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "coinbase-pro": "^0.9.0", "csv-parse": "^4.15.4", "csv-parser": "^2.3.3", - "dotenv": "^8.2.0", + "dotenv": "^8.6.0", "node-csv": "^0.1.2", "pino": "^6.11.3" }, "devDependencies": { - "eslint": "^7.24.0" + "eslint": "^7.27.0" } } diff --git a/strategies/momentumTrading/momentumTrading.js b/strategies/momentumTrading/momentumTrading.js index 66f5f7f..475ed43 100644 --- a/strategies/momentumTrading/momentumTrading.js +++ b/strategies/momentumTrading/momentumTrading.js @@ -1,6 +1,6 @@ const CoinbasePro = require("coinbase-pro"); require('dotenv').config() -const { buyPosition, sellPosition } = require("./buyAndSell"); +const { buyPosition, sellPosition } = require("../../buyAndSell"); const coinbaseProLib = require("../../coinbaseProLibrary"); const pino = require("pino"); const logger = pino({ level: process.env.LOG_LEVEL || "info" }); diff --git a/strategies/reverseMomentumTrading/buyAndSell.js b/strategies/reverseMomentumTrading/buyAndSell.js deleted file mode 100644 index 673de89..0000000 --- a/strategies/reverseMomentumTrading/buyAndSell.js +++ /dev/null @@ -1,224 +0,0 @@ -/* -* This module contains methods to buy a position and sell a position. It uses a limit order then loops checking the order -* status until the order either completes, OR after 1 minute it will cancel the order. -*/ -const pino = require("pino"); -const logger = pino({ level: process.env.LOG_LEVEL || "info" }); -const fileSystem = require("fs"); - -/** - * Halts the program from running temporarily to prevent it from hitting API call limits - * - * @param {number} ms -> the number of miliseconds to wait - */ -function sleep(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - -/** - * Places a sell limit order then loops to check the order status until the order is filled. Once filled, the method updates the positionInfo, does any depositing based on the - * depositConfig, then ends. If the Order is done for a reason other than filled, or a profit was not made then the method throws an exception. If the order doesn't get filled - * in the alloted time span (1 minute) then the method cancels the order and throws an exception. - * - * @param {Number} balance - * @param {Object} accountIds - * @param {Object} positionInfo - * @param {Number} currentPrice - * @param {Object} authedClient - * @param {Object} coinbaseLibObject - * @param {Object} productInfo - * @param {Object} depositConfig - * @param {Object} tradingConfig - */ -async function sellPosition(balance, accountIds, positionInfo, currentPrice, authedClient, coinbaseLibObject, productInfo, depositConfig, tradingConfig) { - try { - const priceToSell = (currentPrice - (currentPrice * tradingConfig.orderPriceDelta)).toFixed(productInfo.quoteIncrementRoundValue); - - let orderSize; - if (productInfo.baseIncrementRoundValue === 0) { - orderSize = Math.trunc(balance); - } else { - orderSize = (balance).toFixed(productInfo.baseIncrementRoundValue); - } - - const orderParams = { - side: "sell", - price: priceToSell, - size: orderSize, - product_id: productInfo.productPair, - time_in_force: "FOK" - }; - - logger.info("Sell order params: " + JSON.stringify(orderParams)); - - //Place sell order - const order = await authedClient.placeOrder(orderParams); - logger.debug(order); - const orderID = order.id; - - //Loop to wait for order to be filled: - for (let i = 0; i < 10 && positionInfo.positionExists === true; ++i) { - let orderDetails; - logger.debug("Checking sell order result..."); - await sleep(6000); //wait 6 seconds - try { - orderDetails = await authedClient.getOrder(orderID); //Get latest order details - } catch (err) { - const message = "Error occured when attempting to get the order."; - const errorMsg = new Error(err); - logger.error({ message, errorMsg, err }); - continue; - } - logger.debug(orderDetails); - - if (orderDetails.status === "done") { - if (orderDetails.done_reason !== "filled") { - throw new Error("Sell order did not complete due to being filled? done_reason: " + orderDetails.done_reason); - } else { - positionInfo.positionExists = false; - - //Update positionData file: - try { - const writeData = JSON.stringify(positionInfo); - fileSystem.writeFileSync("positionData.json", writeData); - } catch (err) { - const message = "Error, failed to write the positionInfo to the positionData file in sellPosition. Continuing as normal but but positionDataTracking might not work correctly."; - const errorMsg = new Error(err); - logger.error({ message, errorMsg, err }); - } - - let profit = parseFloat(orderDetails.executed_value) - parseFloat(orderDetails.fill_fees) - positionInfo.positionAcquiredCost; - logger.info("Profit: " + profit); - - if (profit > 0) { - //Check deposit config: - if (depositConfig.depositingEnabled) { - const transferAmount = (profit * depositConfig.depositingAmount).toFixed(2); - const currency = productInfo.quoteCurrency; - - //Transfer funds to depositProfileID - const transferResult = await coinbaseLibObject.profileTransfer(accountIds.tradeProfileID, accountIds.depositProfileID, currency, transferAmount); - - logger.debug("transfer result: " + transferResult); - } - } else { - throw new Error("Sell was not profitable, terminating program. profit: " + profit); - } - } - } - } - - //Check if order wasn't filled and needs cancelled: - if (positionInfo.positionExists === true) { - const cancelOrder = await authedClient.cancelOrder(orderID); - if (cancelOrder !== orderID) { - throw new Error("Attempted to cancel failed order but it did not work. cancelOrderReturn: " + cancelOrder + "orderID: " + orderID); - } - } - - } catch (err) { - const message = "Error occured in sellPosition method."; - const errorMsg = new Error(err); - logger.error({ message, errorMsg, err }); - } -} - -/** - * This method places a buy limit order and loops waiting for it to be filled. Once filled it will update the positionInfo and end. If the - * order ends for a reason other then filled it will throw an exception. If the order doesn't get filled after 1 minute it will cancel the - * order and throw an exception. - * - * @param {Number} balance - * @param {Object} positionInfo - * @param {Number} currentPrice - * @param {Object} authedClient - * @param {Object} productInfo - * @param {Object} tradingConfig - */ -async function buyPosition(balance, positionInfo, currentPrice, authedClient, productInfo, tradingConfig) { - try { - const amountToSpend = balance - (balance * tradingConfig.highestFee); - const priceToBuy = (currentPrice + (currentPrice * tradingConfig.orderPriceDelta)).toFixed(productInfo.quoteIncrementRoundValue); - let orderSize; - - if (productInfo.baseIncrementRoundValue === 0) { - orderSize = Math.trunc(amountToSpend / priceToBuy); - } else { - orderSize = (amountToSpend / priceToBuy).toFixed(productInfo.baseIncrementRoundValue); - } - - const orderParams = { - side: "buy", - price: priceToBuy, - size: orderSize, - product_id: productInfo.productPair, - time_in_force: "FOK" - }; - - logger.info("Buy order params: " + JSON.stringify(orderParams)); - - //Place buy order - const order = await authedClient.placeOrder(orderParams); - logger.debug(order); - const orderID = order.id; - - //Loop to wait for order to be filled: - for (let i = 0; i < 10 && positionInfo.positionExists === false; ++i) { - let orderDetails; - logger.debug("Checking buy order result..."); - await sleep(6000); //wait 6 seconds - try { - orderDetails = await authedClient.getOrder(orderID); //Get latest order details - } catch (err) { - const message = "Error occured when attempting to get the order."; - const errorMsg = new Error(err); - logger.error({ message, errorMsg, err }); - continue; - } - logger.debug(orderDetails); - - if (orderDetails.status === "done") { - if (orderDetails.done_reason !== "filled") { - throw new Error("Buy order did not complete due to being filled? done_reason: " + orderDetails.done_reason); - } else { - //Update position info - positionInfo.positionExists = true; - positionInfo.positionAcquiredPrice = parseFloat(orderDetails.executed_value) / parseFloat(orderDetails.filled_size); - positionInfo.positionAcquiredCost = parseFloat(orderDetails.executed_value) + parseFloat(orderDetails.fill_fees); - - //Update positionData file: - try { - const writeData = JSON.stringify(positionInfo); - fileSystem.writeFileSync("positionData.json", writeData); - } catch (err) { - const message = "Error, failed to write the positionInfo to the positionData file in buyPosition. Continuing as normal but but positionDataTracking might not work correctly."; - const errorMsg = new Error(err); - logger.error({ message, errorMsg, err }); - } - - logger.info(positionInfo); - } - } - } - - //Check if order wasn't filled and needs cancelled - if (positionInfo.positionExists === false) { - const cancelOrder = await authedClient.cancelOrder(orderID); - if (cancelOrder !== orderID) { - throw new Error("Attempted to cancel failed order but it did not work. cancelOrderReturn: " + cancelOrder + "orderID: " + orderID); - } - } - - } catch (err) { - const message = "Error occured in buyPosition method."; - const errorMsg = new Error(err); - logger.error({ message, errorMsg, err }); - } -} - -module.exports = { - sellPosition, - buyPosition, -} \ No newline at end of file diff --git a/strategies/reverseMomentumTrading/reverseMomentumTrading.js b/strategies/reverseMomentumTrading/reverseMomentumTrading.js index fa3538d..9de195a 100644 --- a/strategies/reverseMomentumTrading/reverseMomentumTrading.js +++ b/strategies/reverseMomentumTrading/reverseMomentumTrading.js @@ -1,6 +1,6 @@ const CoinbasePro = require("coinbase-pro"); require('dotenv').config() -const { buyPosition, sellPosition } = require("./buyAndSell"); +const { buyPosition, sellPosition } = require("../../buyAndSell"); const coinbaseProLib = require("../../coinbaseProLibrary"); const pino = require("pino"); const logger = pino({ level: process.env.LOG_LEVEL || "info" });