Resolve "Alpaca API Implementation" #28

Merged
clbertolini merged 9 commits from 12-alpaca-api-implementation into main 2023-10-24 20:54:40 +00:00
14 changed files with 614 additions and 246 deletions

View File

@ -1,136 +1,137 @@
module.exports = { module.exports = {
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
ecmaVersion: "latest", // Allows the use of modern ECMAScript features ecmaVersion: "latest", // Allows the use of modern ECMAScript features
sourceType: "module", // Allows for the use of imports sourceType: "module", // Allows for the use of imports
project: ["./tsconfig.json", "./tsconfig.test.json"] project: ["./tsconfig.json", "./tsconfig.test.json"]
}, },
extends: ["plugin:@typescript-eslint/recommended"], // Uses the linting rules from @typescript-eslint/eslint-plugin extends: ["plugin:@typescript-eslint/recommended"], // Uses the linting rules from @typescript-eslint/eslint-plugin
env: { env: {
node: true, // Enable Node.js global variables node: true, // Enable Node.js global variables
}, },
rules: { rules: {
"array-callback-return": "error", "array-callback-return": "error",
"no-await-in-loop": "error", "no-await-in-loop": "error",
"no-cond-assign": [ "error", "always" ], "no-cond-assign": ["error", "always"],
"no-constant-binary-expression": "error", "no-constant-binary-expression": "error",
"no-constant-condition": "error", "no-constant-condition": "error",
"no-constructor-return": "error", "no-constructor-return": "error",
"no-duplicate-imports": "error", "no-duplicate-imports": "error",
"no-promise-executor-return": "error", "no-promise-executor-return": "error",
"no-self-compare": "error", "no-self-compare": "error",
"no-undef": [ "error", { "typeof": true } ], "no-undef": ["error", { "typeof": true }],
"no-unmodified-loop-condition": "error", "no-unmodified-loop-condition": "error",
"no-unreachable-loop": "error", "no-unreachable-loop": "error",
"no-unused-private-class-members": "error", "no-unused-private-class-members": "error",
"require-atomic-updates": "error", "require-atomic-updates": "error",
"accessor-pairs": "error", "accessor-pairs": "error",
"block-scoped-var": "error", "block-scoped-var": "error",
"curly": [ "error", "all" ], "curly": ["error", "all"],
"default-case": "error", "default-case": "error",
"default-case-last": "error", "default-case-last": "error",
"default-param-last": "error", "default-param-last": "error",
"dot-notation": "error", "dot-notation": "error",
"eqeqeq": "error", "eqeqeq": "error",
"func-style": [ "error", "expression" ], "func-style": ["error", "expression"],
"grouped-accessor-pairs": [ "error", "getBeforeSet" ], "grouped-accessor-pairs": ["error", "getBeforeSet"],
"no-caller": "error", "no-caller": "error",
"no-console": "error", "no-console": "error",
"no-eq-null": "error", "no-eq-null": "error",
"no-eval": "error", "no-eval": "error",
"no-extend-native": "error", "no-extend-native": "error",
"no-extra-label": "error", "no-extra-label": "error",
"no-implicit-coercion": "error", "no-implicit-coercion": "error",
"no-implicit-globals": "error", "no-implicit-globals": "error",
"no-implied-eval": "error", "no-implied-eval": "error",
"no-inline-comments": "error", "no-inline-comments": "error",
"no-invalid-this": "error", "no-invalid-this": "error",
"no-iterator": "error", "no-iterator": "error",
"no-label-var": "error", "no-label-var": "error",
"no-labels": "error", "no-labels": "error",
"no-lone-blocks": "error", "no-lone-blocks": "error",
"no-lonely-if": "error", "no-lonely-if": "error",
"no-loop-func": "error", "no-loop-func": "error",
"no-magic-numbers": "error", "no-magic-numbers": "error",
"no-mixed-operators": "error", "no-mixed-operators": "error",
"no-multi-assign": "error", "no-multi-assign": "error",
"no-multi-str": "error", "no-multi-str": "error",
"no-nested-ternary": "error", "no-nested-ternary": "error",
"no-new": "error", "no-new": "error",
"no-new-func": "error", "no-new-func": "error",
"no-new-wrappers": "error", "no-new-wrappers": "error",
"no-nonoctal-decimal-escape": "error", "no-nonoctal-decimal-escape": "error",
"no-object-constructor": "error", "no-object-constructor": "error",
"no-octal-escape": "error", "no-octal-escape": "error",
"no-param-reassign": "error", "no-param-reassign": "error",
"no-plusplus": "error", "no-plusplus": "error",
"no-proto": "error", "no-proto": "error",
"no-return-assign": "error", "no-return-assign": "error",
"no-script-url": "error", "no-script-url": "error",
"no-sequences": "error", "no-sequences": "error",
"no-shadow": "error", "no-shadow": "error",
"no-throw-literal": "error", "no-throw-literal": "error",
"no-undef-init": "error", "no-undef-init": "error",
"no-undefined": "error", "no-undefined": "error",
"no-underscore-dangle": "error", "no-underscore-dangle": "error",
"no-unneeded-ternary": "error", "no-unneeded-ternary": "error",
"no-unused-expressions": "error", "no-unused-expressions": "error",
"no-useless-call": "error", "no-useless-call": "error",
"no-useless-catch": "error", "no-useless-catch": "error",
"no-useless-computed-key": "error", "no-useless-computed-key": "error",
"no-useless-concat": "error", "no-useless-concat": "error",
"no-useless-constructor": "error", "no-useless-constructor": "error",
"no-useless-rename": "error", "no-useless-rename": "error",
"no-var": "error", "no-var": "error",
"no-void": "error", "no-void": "error",
"one-var": [ "error", "never" ], "one-var": ["error", "never"],
"one-var-declaration-per-line": [ "error", "always" ], "one-var-declaration-per-line": ["error", "always"],
"prefer-arrow-callback": "error", "prefer-arrow-callback": "error",
"prefer-const": "error", "prefer-const": "error",
"prefer-exponentiation-operator": "error", "prefer-exponentiation-operator": "error",
"prefer-named-capture-group": "error", "prefer-named-capture-group": "error",
"prefer-numeric-literals": "error", "prefer-numeric-literals": "error",
"prefer-object-has-own": "error", "prefer-object-has-own": "error",
"prefer-object-spread": "error", "prefer-object-spread": "error",
"prefer-promise-reject-errors": "error", "prefer-promise-reject-errors": "error",
"prefer-regex-literals": "error", "prefer-regex-literals": "error",
"prefer-rest-params": "error", "prefer-rest-params": "error",
"prefer-spread": "error", "prefer-spread": "error",
"prefer-template": "error", "prefer-template": "error",
"quote-props": [ "error", "as-needed" ], "quote-props": ["error", "as-needed"],
"radix": "error", "semi": ["error", "always"],
"require-await": "error", "radix": "error",
"require-unicode-regexp": "error", "require-await": "error",
"symbol-description": "error", "require-unicode-regexp": "error",
"yoda": "error", "symbol-description": "error",
"yoda": "error",
"array-bracket-newline": [ "error", "consistent" ],
"array-bracket-spacing": [ "error", "always", { "singleValue": false } ], "array-bracket-newline": ["error", "consistent"],
"array-element-newline": [ "error", "consistent" ], "array-bracket-spacing": ["error", "always", { "singleValue": false }],
"arrow-parens": "error", "array-element-newline": ["error", "consistent"],
"arrow-spacing": "error", "arrow-parens": "error",
"block-spacing": "error", "arrow-spacing": "error",
"computed-property-spacing": "error", "block-spacing": "error",
"eol-last": "error", "computed-property-spacing": "error",
"generator-star-spacing": [ "error", { "before": false, "after": true } ], "eol-last": "error",
"key-spacing": "error", "generator-star-spacing": ["error", { "before": false, "after": true }],
"linebreak-style": "error", "key-spacing": "error",
"lines-around-comment": [ "error", { "allowBlockStart": true }], "linebreak-style": "error",
"max-len": [ "error", { "code": 120 } ], "lines-around-comment": ["error", { "allowBlockStart": true }],
"new-parens": "error", "max-len": ["error", { "code": 120 }],
"no-multi-spaces": "error", "new-parens": "error",
"no-multiple-empty-lines": "error", "no-multi-spaces": "error",
"no-trailing-spaces": "error", "no-multiple-empty-lines": "error",
"no-whitespace-before-property": "error", "no-trailing-spaces": "error",
"nonblock-statement-body-position": [ "error", "below" ], "no-whitespace-before-property": "error",
"object-curly-newline": [ "error", { "ObjectExpression": { "consistent": true }, "ObjectPattern": { "consistent": true } } ], "nonblock-statement-body-position": ["error", "below"],
"operator-linebreak": "error", "object-curly-newline": ["error", { "ObjectExpression": { "consistent": true }, "ObjectPattern": { "consistent": true } }],
"padded-blocks": [ "error", "never" ], "operator-linebreak": "error",
"space-in-parens": "error", "padded-blocks": ["error", "never"],
"switch-colon-spacing": [ "error", { "after": false, "before": false } ], "space-in-parens": "error",
"template-curly-spacing": "error", "switch-colon-spacing": ["error", { "after": false, "before": false }],
"wrap-iife": "error", "template-curly-spacing": "error",
"yield-star-spacing": [ "error", "after" ], "wrap-iife": "error",
} "yield-star-spacing": ["error", "after"],
}; }
};

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules/** node_modules/**
dist/** dist/**
docs/** docs/**
.env

View File

@ -8,6 +8,7 @@ const config: Config.InitialOptions =
}, },
collectCoverageFrom: ['src/*.ts'], collectCoverageFrom: ['src/*.ts'],
verbose: false,
} }
export default config export default config

255
package-lock.json generated
View File

@ -9,6 +9,8 @@
"version": "1.0.0", "version": "1.0.0",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
"@alpacahq/alpaca-trade-api": "^3.0.1",
"dotenv": "^16.3.1",
"winston": "^3.10.0" "winston": "^3.10.0"
}, },
"devDependencies": { "devDependencies": {
@ -34,6 +36,35 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@alpacahq/alpaca-trade-api": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@alpacahq/alpaca-trade-api/-/alpaca-trade-api-3.0.1.tgz",
"integrity": "sha512-FOsgA4jREAArWbb7nuKTo2yiUdb8+/4vboyTX2SRJSi+oYBaFrZV+hMNx7fGWtuLuKGRKEdK8Nc+8MFjwcbz4g==",
"dependencies": {
"axios": "^0.21.1",
"dotenv": "^6.2.0",
"events": "^3.2.0",
"just-extend": "^4.1.0",
"lodash": "^4.17.19",
"minimist": "^1.2.6",
"msgpack5": "^5.3.2",
"nats": "^1.4.9",
"urljoin": "^0.1.5",
"ws": "^7.4.3"
},
"engines": {
"node": ">=14.x",
"npm": ">=6"
}
},
"node_modules/@alpacahq/alpaca-trade-api/node_modules/dotenv": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz",
"integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==",
"engines": {
"node": ">=6"
}
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
@ -662,9 +693,9 @@
} }
}, },
"node_modules/@babel/traverse": { "node_modules/@babel/traverse": {
"version": "7.23.0", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.22.13", "@babel/code-frame": "^7.22.13",
@ -1822,6 +1853,14 @@
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
}, },
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/babel-jest": { "node_modules/babel-jest": {
"version": "29.7.0", "version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@ -1944,6 +1983,35 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "dev": true
}, },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -2019,6 +2087,29 @@
"node-int64": "^0.4.0" "node-int64": "^0.4.0"
} }
}, },
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -2346,6 +2437,17 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.544", "version": "1.4.544",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz",
@ -2565,6 +2667,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/execa": { "node_modules/execa": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -2613,6 +2723,11 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0" "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
} }
}, },
"node_modules/extend": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-2.0.2.tgz",
"integrity": "sha512-AgFD4VU+lVLP6vjnlNfF7OeInLTyeyckCNPEsuxz1vi786UuK/nk6ynPuhn/h+Ju9++TQyr5EpLRI14fc1QtTQ=="
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -2747,6 +2862,25 @@
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
}, },
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -2918,6 +3052,25 @@
"node": ">=10.17.0" "node": ">=10.17.0"
} }
}, },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -3770,6 +3923,11 @@
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true "dev": true
}, },
"node_modules/just-extend": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg=="
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.3", "version": "4.5.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
@ -3836,6 +3994,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.memoize": { "node_modules/lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -3970,11 +4133,48 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/msgpack5": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-5.3.2.tgz",
"integrity": "sha512-e9jz+6InQIUb2cGzZ/Mi+rQBs1KFLby+gNi+22VwQ1pnC9ieZjysKGmRMjdlf6IyjsltbgY4tGoHwHmyS7l94A==",
"dependencies": {
"bl": "^4.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.0",
"safe-buffer": "^5.1.2"
}
},
"node_modules/nats": {
"version": "1.4.12",
"resolved": "https://registry.npmjs.org/nats/-/nats-1.4.12.tgz",
"integrity": "sha512-Jf4qesEF0Ay0D4AMw3OZnKMRTQm+6oZ5q8/m4gpy5bTmiDiK6wCXbZpzEslmezGpE93LV3RojNEG6dpK/mysLQ==",
"dependencies": {
"nuid": "^1.1.4",
"ts-nkeys": "^1.0.16"
},
"bin": {
"node-pub": "examples/node-pub",
"node-reply": "examples/node-reply",
"node-req": "examples/node-req",
"node-sub": "examples/node-sub"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/natural-compare": { "node_modules/natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -4014,6 +4214,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/nuid": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.6.tgz",
"integrity": "sha512-Eb3CPCupYscP1/S1FQcO5nxtu6l/F3k0MQ69h7f5osnsemVk5pkc8/5AyalVT+NCfra9M71U8POqF6EZa6IHvg==",
"engines": {
"node": ">= 8.16.0"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -4864,6 +5072,14 @@
} }
} }
}, },
"node_modules/ts-nkeys": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz",
"integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==",
"dependencies": {
"tweetnacl": "^1.0.3"
}
},
"node_modules/ts-node": { "node_modules/ts-node": {
"version": "10.9.1", "version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@ -4907,6 +5123,11 @@
} }
} }
}, },
"node_modules/tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@ -5037,6 +5258,14 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/urljoin": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/urljoin/-/urljoin-0.1.5.tgz",
"integrity": "sha512-OSGi+PS3zxk8XfQ+7buaupOdrW9P9p+V9rjxGzJaYEYDe/B2rv3WJCupq5LNERW4w4kWxsduUUrhCxZZiQ2udw==",
"dependencies": {
"extend": "~2.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -5168,6 +5397,26 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -28,6 +28,8 @@
"typescript": "^5.2.2" "typescript": "^5.2.2"
}, },
"dependencies": { "dependencies": {
"@alpacahq/alpaca-trade-api": "^3.0.1",
"dotenv": "^16.3.1",
"winston": "^3.10.0" "winston": "^3.10.0"
} }
} }

51
src/alpaca/exchange.ts Normal file
View File

@ -0,0 +1,51 @@
import Alpaca from '@alpacahq/alpaca-trade-api';
import { AlpacaPortfolioProvider } from './portfolio';
import { AlpacaQuoteProvider } from './quote';
import { Exchange } from '../interface/exchange';
import { PortfolioProvider } from '../interface/portfolio';
import { QuoteProvider } from '../interface/quote';
/**
* Exchange implementation for Alpaca.
*/
export class AlpacaExchange implements Exchange {
/**
* The Alpaca API client.
*/
readonly alpaca: Alpaca;
/**
* The portfolio provider for the exchange.
*/
readonly portfolioProvider: PortfolioProvider;
/**
* The quote provider for the exchange.
*/
readonly quoteProvider: QuoteProvider;
/**
* The name of the exchange.
*/
readonly name: string;
/**
* Creates an instance of the Alpaca exchange.
* @constructor
* @param {string} keyId - The API key ID for the Alpaca exchange.
* @param {string} secretKey - The secret key for the Alpaca exchange.
* @param {boolean} paper - Whether to use the paper trading environment or not.
*/
constructor(keyId: string, secretKey: string, paper: boolean) {
this.alpaca = new Alpaca({
keyId: keyId,
secretKey: secretKey,
paper: paper
});
this.portfolioProvider = new AlpacaPortfolioProvider(this.alpaca);
this.quoteProvider = new AlpacaQuoteProvider(this.alpaca);
this.name = 'Alpaca';
}
}

63
src/alpaca/portfolio.ts Normal file
View File

@ -0,0 +1,63 @@
import Alpaca from '@alpacahq/alpaca-trade-api';
import { PortfolioProvider, Portfolio, Position } from '../interface/portfolio';
/**
* The position object returned by Alpaca.
* @see https://alpaca.markets/docs/api-references/trading-api/positions/#properties
*/
class AlpacaPosition {
asset_id!: string;
symbol!: string;
exchange!: string;
asset_class!: string;
avg_entry_price!: string;
qty!: string;
qty_available!: string;
side!: string;
market_value!: string;
cost_basis!: string;
unrealized_pl!: string;
unrealized_plpc!: string;
unrealized_intraday_pl!: string;
unrealized_intraday_plpc!: string;
current_price!: string;
lastday_price!: string;
change_today!: string;
asset_marginable!: string;
}
/**
* Provides a portfolio using the Alpaca API client.
*/
export class AlpacaPortfolioProvider implements PortfolioProvider {
/**
* The Alpaca API client.
*/
readonly alpaca: Alpaca;
/**
* Fetches the portfolio.
*/
readonly fetchPortfolio = (): Promise<Portfolio> => {
return (this.alpaca.getPositions() as Promise<AlpacaPosition[]>).then((positions) => {
return new Portfolio(positions.map((position) => {
return new Position(
position.symbol,
parseInt(position.qty, 10),
parseFloat(position.market_value),
parseFloat(position.cost_basis),
parseFloat(position.market_value)
);
}));
});
};
/**
* Creates a new AlpacaPortfolioProvider instance.
* @constructor
* @param {Alpaca} alpaca - The Alpaca API client.
*/
constructor(alpaca: Alpaca) {
this.alpaca = alpaca;
}
}

41
src/alpaca/quote.ts Normal file
View File

@ -0,0 +1,41 @@
import Alpaca from '@alpacahq/alpaca-trade-api';
import { QuoteProvider, Quote } from '../interface/quote';
/**
* Provides quotes using the Alpaca API.
*/
export class AlpacaQuoteProvider implements QuoteProvider {
/**
* The Alpaca API client.
*/
readonly alpaca: Alpaca;
/**
* Fetches a quote for the given stock symbol.
* @param symbol The stock symbol to fetch the quote for.
* @returns A Promise that resolves to a Quote object.
*/
readonly fetchQuote = (symbol: string): Promise<Quote> => {
return this.alpaca.getLatestQuote(symbol).then((quote) => {
return new Quote(
quote.Symbol,
quote.AskPrice,
quote.AskSize,
quote.BidPrice,
quote.BidSize,
new Date(Date.parse(quote.Timestamp))
);
}).catch((err) => {
return err;
});
};
/**
* Creates a new AlpacaQuoteProvider instance.
* @constructor
* @param {Alpaca} alpaca - The Alpaca API client.
*/
constructor(alpaca: Alpaca) {
this.alpaca = alpaca;
}
}

View File

@ -9,4 +9,4 @@ export {
PortfolioProvider, PortfolioProvider,
Quote, Quote,
QuoteProvider QuoteProvider
} };

View File

@ -20,4 +20,4 @@ export interface Exchange {
* The name of the exchange. * The name of the exchange.
*/ */
readonly name: string; readonly name: string;
} }

View File

@ -3,67 +3,46 @@
*/ */
export class Position { export class Position {
/** /**
* The price of the last trade made for this position. * The symbol name of the asset
*/
readonly lastTrade: number;
/**
* The date and time of the last trade made for this position.
*/
readonly lastTradeTime: Date;
/**
* The change in the position value.
*/
readonly change: number;
/**
* The percentage change in the position value.
*/
readonly changePercent: number;
/**
* The earnings per share of the position.
*/
readonly earningsPerShare: number;
/**
* The market capitalization of the position.
*/
readonly marketCap: number;
/**
* The symbol of the position.
*/ */
readonly symbol: string; readonly symbol: string;
/** /**
* Represents a position in a portfolio. * The total number of shares, not including open orders
* @constructor
* @param {number} lastTrade - The price of the last trade made for this position.
* @param {Date} lastTradeTime - The date and time of the last trade made for this position.
* @param {number} change - The change in the position value.
* @param {number} changePercent - The percentage change in the position value.
* @param {number} earningsPerShare - The earnings per share of the position.
* @param {number} marketCap - The market capitalization of the position.
* @param {string} symbol - The symbol of the position.
*/ */
constructor( readonly quantity: number;
lastTrade: number,
lastTradeTime: Date, /**
change: number, * The total dollar amount of the position
changePercent: number, */
earningsPerShare: number, readonly marketValue: number;
marketCap: number,
symbol: string /**
) { * The total cost basis
this.lastTrade = lastTrade; */
this.lastTradeTime = lastTradeTime; readonly costBasis: number;
this.change = change;
this.changePercent = changePercent; /**
this.earningsPerShare = earningsPerShare; * The current asset price per share
this.marketCap = marketCap; */
readonly pricePerShare: number;
/**
* Creates a new Portfolio object.
* @constructor
* @param {string} symbol - The symbol of the asset in the portfolio.
* @param {number} quantity - The quantity of the asset in the portfolio.
* @param {number} marketValue - The market value of the asset in the portfolio.
* @param {number} costBasis - The cost basis of the asset in the portfolio.
* @param {number} pricePerShare - The price per share of the asset in the portfolio.
*/
constructor(symbol: string, quantity: number, marketValue: number, costBasis: number, pricePerShare: number) {
this.symbol = symbol; this.symbol = symbol;
this.quantity = quantity;
this.marketValue = marketValue;
this.costBasis = costBasis;
this.pricePerShare = pricePerShare;
} }
} }

View File

@ -2,52 +2,20 @@
* Represents a stock quote. * Represents a stock quote.
*/ */
export class Quote { export class Quote {
/**
* The name of the company associated with this quote.
*/
readonly companyName: string;
/**
* The earnings per share of a company.
*/
readonly earningsPerShare: number;
/**
* The estimated earnings for a stock.
*/
readonly estimatedEarnings: number;
/**
* The price of the last trade for the security.
*/
readonly lastTrade: number;
/**
* The symbol of the financial instrument being quoted.
*/
readonly symbol: string; readonly symbol: string;
readonly askPrice: number;
readonly askSize: number;
readonly bidPrice: number;
readonly bidSize: number;
readonly timeStamp: Date;
/** constructor(symbol: string, askPrice: number, askSize: number, bidPrice: number, bidSize: number, timeStamp: Date) {
* Represents a quote for a particular stock.
* @constructor
* @param {string} companyName - The name of the company associated with the stock.
* @param {number} earningsPerShare - The earnings per share for the stock.
* @param {number} estimatedEarnings - The estimated earnings for the stock.
* @param {number} lastTrade - The last trade price for the stock.
* @param {string} symbol - The symbol for the stock.
*/
constructor(
companyName: string,
earningsPerShare: number,
estimatedEarnings: number,
lastTrade: number,
symbol: string
) {
this.companyName = companyName;
this.earningsPerShare = earningsPerShare;
this.estimatedEarnings = estimatedEarnings;
this.lastTrade = lastTrade;
this.symbol = symbol; this.symbol = symbol;
this.askPrice = askPrice;
this.askSize = askSize;
this.bidPrice = bidPrice;
this.bidSize = bidSize;
this.timeStamp = timeStamp;
} }
} }

19
test/alpaca.test.ts Normal file
View File

@ -0,0 +1,19 @@
import { describe, expect, test } from '@jest/globals';
import 'dotenv/config';
import { AlpacaExchange } from '../src/alpaca/exchange';
describe('Alpaca Tests', () => {
test('portfolio fetch', () => {
expect(process.env.ALPACA_API_KEY).toBeDefined();
expect(process.env.ALPACA_SECRET_KEY).toBeDefined();
const exchange = new AlpacaExchange(process.env.ALPACA_API_KEY!, process.env.ALPACA_SECRET_KEY!, true);
expect(exchange.portfolioProvider.fetchPortfolio()).resolves.toBeDefined();
});
test('quote fetch', () => {
expect(process.env.ALPACA_API_KEY).toBeDefined();
expect(process.env.ALPACA_SECRET_KEY).toBeDefined();
const exchange = new AlpacaExchange(process.env.ALPACA_API_KEY!, process.env.ALPACA_SECRET_KEY!, true);
expect(exchange.quoteProvider.fetchQuote("AAPL")).resolves.toBeDefined();
});
});

View File

@ -1,7 +0,0 @@
import {describe, expect, test} from '@jest/globals';
describe('sum module', () => {
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});