Resolve "Alpaca API Implementation" #28
@ -99,6 +99,7 @@ module.exports = {
|
||||
"prefer-spread": "error",
|
||||
"prefer-template": "error",
|
||||
"quote-props": ["error", "as-needed"],
|
||||
"semi": ["error", "always"],
|
||||
"radix": "error",
|
||||
"require-await": "error",
|
||||
"require-unicode-regexp": "error",
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
node_modules/**
|
||||
dist/**
|
||||
docs/**
|
||||
.env
|
@ -8,6 +8,7 @@ const config: Config.InitialOptions =
|
||||
},
|
||||
|
||||
collectCoverageFrom: ['src/*.ts'],
|
||||
verbose: false,
|
||||
}
|
||||
|
||||
export default config
|
255
package-lock.json
generated
255
package-lock.json
generated
@ -9,6 +9,8 @@
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@alpacahq/alpaca-trade-api": "^3.0.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"winston": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -34,6 +36,35 @@
|
||||
"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": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
||||
@ -662,9 +693,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz",
|
||||
"integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
@ -1822,6 +1853,14 @@
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
|
||||
"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": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
@ -1944,6 +1983,35 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"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": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@ -2019,6 +2087,29 @@
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@ -2346,6 +2437,17 @@
|
||||
"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": {
|
||||
"version": "1.4.544",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz",
|
||||
@ -2565,6 +2667,14 @@
|
||||
"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": {
|
||||
"version": "5.1.1",
|
||||
"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_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": {
|
||||
"version": "3.1.3",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@ -2918,6 +3052,25 @@
|
||||
"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": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
@ -3770,6 +3923,11 @@
|
||||
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
|
||||
"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": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
|
||||
@ -3836,6 +3994,11 @@
|
||||
"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": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
@ -3970,11 +4133,48 @@
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"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": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@ -4014,6 +4214,14 @@
|
||||
"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": {
|
||||
"version": "1.4.0",
|
||||
"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": {
|
||||
"version": "10.9.1",
|
||||
"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": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@ -5037,6 +5258,14 @@
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"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_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": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
@ -28,6 +28,8 @@
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alpacahq/alpaca-trade-api": "^3.0.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"winston": "^3.10.0"
|
||||
}
|
||||
}
|
||||
|
51
src/alpaca/exchange.ts
Normal file
51
src/alpaca/exchange.ts
Normal 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
63
src/alpaca/portfolio.ts
Normal 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
41
src/alpaca/quote.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -9,4 +9,4 @@ export {
|
||||
PortfolioProvider,
|
||||
Quote,
|
||||
QuoteProvider
|
||||
}
|
||||
};
|
||||
|
@ -3,67 +3,46 @@
|
||||
*/
|
||||
export class Position {
|
||||
/**
|
||||
* The price of the last trade made for this position.
|
||||
*/
|
||||
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.
|
||||
* The symbol name of the asset
|
||||
*/
|
||||
readonly symbol: string;
|
||||
|
||||
/**
|
||||
* Represents a position in a portfolio.
|
||||
* @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.
|
||||
* The total number of shares, not including open orders
|
||||
*/
|
||||
constructor(
|
||||
lastTrade: number,
|
||||
lastTradeTime: Date,
|
||||
change: number,
|
||||
changePercent: number,
|
||||
earningsPerShare: number,
|
||||
marketCap: number,
|
||||
symbol: string
|
||||
) {
|
||||
this.lastTrade = lastTrade;
|
||||
this.lastTradeTime = lastTradeTime;
|
||||
this.change = change;
|
||||
this.changePercent = changePercent;
|
||||
this.earningsPerShare = earningsPerShare;
|
||||
this.marketCap = marketCap;
|
||||
readonly quantity: number;
|
||||
|
||||
/**
|
||||
* The total dollar amount of the position
|
||||
*/
|
||||
readonly marketValue: number;
|
||||
|
||||
/**
|
||||
* The total cost basis
|
||||
*/
|
||||
readonly costBasis: number;
|
||||
|
||||
/**
|
||||
* The current asset price per share
|
||||
*/
|
||||
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.quantity = quantity;
|
||||
this.marketValue = marketValue;
|
||||
this.costBasis = costBasis;
|
||||
this.pricePerShare = pricePerShare;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,52 +2,20 @@
|
||||
* Represents a stock 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 askPrice: number;
|
||||
readonly askSize: number;
|
||||
readonly bidPrice: number;
|
||||
readonly bidSize: number;
|
||||
readonly 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;
|
||||
constructor(symbol: string, askPrice: number, askSize: number, bidPrice: number, bidSize: number, timeStamp: Date) {
|
||||
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
19
test/alpaca.test.ts
Normal 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();
|
||||
});
|
||||
});
|
@ -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);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user