├── Examples ├── FTX-WebSocket-Template │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json └── OKX-WebSocket-Template │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json └── README.md /Examples/FTX-WebSocket-Template/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Examples/FTX-WebSocket-Template/README.md: -------------------------------------------------------------------------------- 1 | # FTX Websocket Template built with PENDAX-SDK 2 | https://github.com/CompendiumFi/PENDAX-SDK 3 | 4 | install required packages with ```npm install``` 5 | 6 | 7 | run with ```node index.js``` 8 | 9 | ## This application is a fully functional template for FTX websockets utilizing PENDAX-SDK 10 | 11 | Add your api key, secret, and subaccount in index.js under the socket config. adjust parameters as neccesary. -------------------------------------------------------------------------------- /Examples/FTX-WebSocket-Template/index.js: -------------------------------------------------------------------------------- 1 | import { FtxSocket } from "./node_modules/@compendiumfi/pendax/sockets/ftxsocket.js" 2 | // Standalone implementation of FTX sockets 3 | 4 | let ftxSocket; 5 | let subscriptions = {} 6 | // Create config object - we will only use this object once - when the socket is originally started 7 | const SocketConfigOne = { 8 | name: '', 9 | key: "", 10 | secret: "", 11 | subaccount: "", 12 | clientReconnect: socketReconnect, 13 | clientOnOpen: ftxOnOpen, 14 | clientOnMessage: ftxOnMessage, 15 | clientOnError: ftxOnError, 16 | clientOnClose: ftxOnClose, 17 | pingInterval: 10000, 18 | autoReconnectOnClose: true, 19 | reconnectWaitTime: 2000, 20 | maxRetries: 3, 21 | socket: ftxSocket 22 | } 23 | 24 | //Then, for convenience we define a function called startSocket - when called it will take our socketConfig OR the config object from the socket being retried as a parameter 25 | function startSocket(socketConfig) { 26 | // connect 27 | try { 28 | // From this point forward we will access and manage the socket using the returned mySocket object 29 | ftxSocket = new FtxSocket(socketConfig) 30 | // setTimeout( 31 | // function(subscriptions, ftxSocket){cancelSubscriptions(subscriptions, ftxSocket)} , 30000, subscriptions, ftxSocket 32 | // ); 33 | } 34 | catch (error) { 35 | // handle error 36 | } 37 | } 38 | function cancelSubscriptions(subscriptionsToCancel, theSocket){ 39 | for(const subscription in subscriptionsToCancel){ 40 | theSocket.unsubscribe(subscription) 41 | } 42 | } 43 | // Reconnection/retry handler 44 | function socketReconnect (oldSocket) { 45 | // Retrieve the most recent retry number from the old socket 46 | let currentRetry = oldSocket.getRetryNumber(); 47 | // Retrieve the max retries value from the old socket 48 | let maxRetries = oldSocket.getOptions().maxRetries; 49 | // Test to see if we should retry the connection 50 | if (currentRetry <= maxRetries){ 51 | console.log('reconnecting') 52 | // Now retrieve the options object from the old socket 53 | const oldSocketOptions = oldSocket.getOptions() 54 | // Finally attempt to restart the socket, passing in oldSocketOptions instead of mySocketConfig as we did the on first startup 55 | startSocket(oldSocketOptions); 56 | } 57 | else{ 58 | // We ran out of retries...let the socket die 59 | console.log('max socket reconnect retries exceeded - socket terminated') 60 | } 61 | } 62 | // Event handlers 63 | function ftxOnOpen(socketInfo){ 64 | // upon opening we will login (since we are going to subscribe to private data) 65 | // there will be no response in any case 66 | ftxSocket.login(); 67 | console.log('Socket opened - attempting login') 68 | // create our first subscription 69 | subscriptions.myOrders = { 70 | name: 'myOrders', 71 | args: 72 | { channel: 'orders', market: 'BTC-PERP'} 73 | } 74 | // create our second subscription 75 | subscriptions.myFills = { 76 | name: 'myFills', 77 | args: {channel: 'fills', market: 'BTC-PERP'} 78 | } 79 | // create our third subscription 80 | subscriptions.myTickers = { 81 | name: 'myTickers', 82 | args: {channel: 'ticker', market: 'BTC-PERP'} 83 | } 84 | 85 | 86 | // subscribe 87 | // ftxSocket.subscribe(subscriptions.myOrders) 88 | // ftxSocket.subscribe(subscriptions.myFills) 89 | ftxSocket.subscribe(subscriptions.myTickers) 90 | console.log('Attempting subscriptions') 91 | } 92 | function ftxOnMessage(socketInfo, msg){ 93 | switch(msg.type){ 94 | case 'error': 95 | console.log('Error code: ', msg.code, ' Message: ', msg.msg) 96 | break; 97 | case 'subscribed': 98 | console.log('Subscribed to channel: ', msg.channel) 99 | break; 100 | case 'unsubscribed': 101 | // test to see if socket has any remaining subscriptions 102 | if (Object.keys(ftxSocket.getSubscriptions()).length == 0){ 103 | // if not then kill socket - you must pass the socket instance itself into the kill routine 104 | ftxSocket.kill(ftxSocket) 105 | } 106 | console.log('Unsubscribed from channel: ', msg.channel) 107 | break; 108 | case 'info': 109 | console.log('Info received: Code: ', msg.code, ' Message: ', msg.msg) 110 | break; 111 | case 'partial': 112 | console.log('Market snapshot: ', msg.data) 113 | break; 114 | case 'update': 115 | console.log('Market data: ', msg.data) 116 | break; 117 | } 118 | } 119 | function ftxOnError(err, socketInfo){ 120 | console.log(err); 121 | } 122 | function ftxOnClose(code, msg, socketInfo){ 123 | // This is where you should end up after 60 seconds 124 | console.log('Code: ', code, ' Message: ', msg); 125 | } 126 | 127 | // start it up 128 | startSocket(SocketConfigOne) 129 | 130 | -------------------------------------------------------------------------------- /Examples/FTX-WebSocket-Template/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ftx-websocket-template", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ftx-websocket-template", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@compendiumfi/pendax": "^1.1.3" 13 | } 14 | }, 15 | "node_modules/@compendiumfi/pendax": { 16 | "version": "1.1.3", 17 | "resolved": "https://registry.npmjs.org/@compendiumfi/pendax/-/pendax-1.1.3.tgz", 18 | "integrity": "sha512-hT5rDYx8yPrnzTudoW6M8JFXxpjZ/5+EsySDcIF3hMrWcnIolGgYtPDv91qahlGqMlYz55jh4AhanDlA79IskA==", 19 | "dependencies": { 20 | "axios": "^0.27.2", 21 | "base64-js": "^1.5.1", 22 | "crypto-js": "^4.1.1", 23 | "dateformat": "^5.0.3", 24 | "dotenv": "^16.0.1", 25 | "lodash": "^4.17.21", 26 | "short-unique-id": "^4.4.4", 27 | "uuidv4": "^6.2.13", 28 | "ws": "^8.7.0" 29 | } 30 | }, 31 | "node_modules/@types/uuid": { 32 | "version": "8.3.4", 33 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", 34 | "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" 35 | }, 36 | "node_modules/asynckit": { 37 | "version": "0.4.0", 38 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 39 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 40 | }, 41 | "node_modules/axios": { 42 | "version": "0.27.2", 43 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", 44 | "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", 45 | "dependencies": { 46 | "follow-redirects": "^1.14.9", 47 | "form-data": "^4.0.0" 48 | } 49 | }, 50 | "node_modules/base64-js": { 51 | "version": "1.5.1", 52 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 53 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 54 | "funding": [ 55 | { 56 | "type": "github", 57 | "url": "https://github.com/sponsors/feross" 58 | }, 59 | { 60 | "type": "patreon", 61 | "url": "https://www.patreon.com/feross" 62 | }, 63 | { 64 | "type": "consulting", 65 | "url": "https://feross.org/support" 66 | } 67 | ] 68 | }, 69 | "node_modules/combined-stream": { 70 | "version": "1.0.8", 71 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 72 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 73 | "dependencies": { 74 | "delayed-stream": "~1.0.0" 75 | }, 76 | "engines": { 77 | "node": ">= 0.8" 78 | } 79 | }, 80 | "node_modules/crypto-js": { 81 | "version": "4.1.1", 82 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 83 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 84 | }, 85 | "node_modules/dateformat": { 86 | "version": "5.0.3", 87 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz", 88 | "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==", 89 | "engines": { 90 | "node": ">=12.20" 91 | } 92 | }, 93 | "node_modules/delayed-stream": { 94 | "version": "1.0.0", 95 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 96 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 97 | "engines": { 98 | "node": ">=0.4.0" 99 | } 100 | }, 101 | "node_modules/dotenv": { 102 | "version": "16.0.3", 103 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 104 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 105 | "engines": { 106 | "node": ">=12" 107 | } 108 | }, 109 | "node_modules/follow-redirects": { 110 | "version": "1.15.2", 111 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 112 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 113 | "funding": [ 114 | { 115 | "type": "individual", 116 | "url": "https://github.com/sponsors/RubenVerborgh" 117 | } 118 | ], 119 | "engines": { 120 | "node": ">=4.0" 121 | }, 122 | "peerDependenciesMeta": { 123 | "debug": { 124 | "optional": true 125 | } 126 | } 127 | }, 128 | "node_modules/form-data": { 129 | "version": "4.0.0", 130 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 131 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 132 | "dependencies": { 133 | "asynckit": "^0.4.0", 134 | "combined-stream": "^1.0.8", 135 | "mime-types": "^2.1.12" 136 | }, 137 | "engines": { 138 | "node": ">= 6" 139 | } 140 | }, 141 | "node_modules/lodash": { 142 | "version": "4.17.21", 143 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 144 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 145 | }, 146 | "node_modules/mime-db": { 147 | "version": "1.52.0", 148 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 149 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 150 | "engines": { 151 | "node": ">= 0.6" 152 | } 153 | }, 154 | "node_modules/mime-types": { 155 | "version": "2.1.35", 156 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 157 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 158 | "dependencies": { 159 | "mime-db": "1.52.0" 160 | }, 161 | "engines": { 162 | "node": ">= 0.6" 163 | } 164 | }, 165 | "node_modules/short-unique-id": { 166 | "version": "4.4.4", 167 | "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz", 168 | "integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==", 169 | "bin": { 170 | "short-unique-id": "bin/short-unique-id", 171 | "suid": "bin/short-unique-id" 172 | } 173 | }, 174 | "node_modules/uuid": { 175 | "version": "8.3.2", 176 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 177 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 178 | "bin": { 179 | "uuid": "dist/bin/uuid" 180 | } 181 | }, 182 | "node_modules/uuidv4": { 183 | "version": "6.2.13", 184 | "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", 185 | "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", 186 | "dependencies": { 187 | "@types/uuid": "8.3.4", 188 | "uuid": "8.3.2" 189 | } 190 | }, 191 | "node_modules/ws": { 192 | "version": "8.11.0", 193 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 194 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 195 | "engines": { 196 | "node": ">=10.0.0" 197 | }, 198 | "peerDependencies": { 199 | "bufferutil": "^4.0.1", 200 | "utf-8-validate": "^5.0.2" 201 | }, 202 | "peerDependenciesMeta": { 203 | "bufferutil": { 204 | "optional": true 205 | }, 206 | "utf-8-validate": { 207 | "optional": true 208 | } 209 | } 210 | } 211 | }, 212 | "dependencies": { 213 | "@compendiumfi/pendax": { 214 | "version": "1.1.3", 215 | "resolved": "https://registry.npmjs.org/@compendiumfi/pendax/-/pendax-1.1.3.tgz", 216 | "integrity": "sha512-hT5rDYx8yPrnzTudoW6M8JFXxpjZ/5+EsySDcIF3hMrWcnIolGgYtPDv91qahlGqMlYz55jh4AhanDlA79IskA==", 217 | "requires": { 218 | "axios": "^0.27.2", 219 | "base64-js": "^1.5.1", 220 | "crypto-js": "^4.1.1", 221 | "dateformat": "^5.0.3", 222 | "dotenv": "^16.0.1", 223 | "lodash": "^4.17.21", 224 | "short-unique-id": "^4.4.4", 225 | "uuidv4": "^6.2.13", 226 | "ws": "^8.7.0" 227 | } 228 | }, 229 | "@types/uuid": { 230 | "version": "8.3.4", 231 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", 232 | "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" 233 | }, 234 | "asynckit": { 235 | "version": "0.4.0", 236 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 237 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 238 | }, 239 | "axios": { 240 | "version": "0.27.2", 241 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", 242 | "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", 243 | "requires": { 244 | "follow-redirects": "^1.14.9", 245 | "form-data": "^4.0.0" 246 | } 247 | }, 248 | "base64-js": { 249 | "version": "1.5.1", 250 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 251 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 252 | }, 253 | "combined-stream": { 254 | "version": "1.0.8", 255 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 256 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 257 | "requires": { 258 | "delayed-stream": "~1.0.0" 259 | } 260 | }, 261 | "crypto-js": { 262 | "version": "4.1.1", 263 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 264 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 265 | }, 266 | "dateformat": { 267 | "version": "5.0.3", 268 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz", 269 | "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==" 270 | }, 271 | "delayed-stream": { 272 | "version": "1.0.0", 273 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 274 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 275 | }, 276 | "dotenv": { 277 | "version": "16.0.3", 278 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 279 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 280 | }, 281 | "follow-redirects": { 282 | "version": "1.15.2", 283 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 284 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" 285 | }, 286 | "form-data": { 287 | "version": "4.0.0", 288 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 289 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 290 | "requires": { 291 | "asynckit": "^0.4.0", 292 | "combined-stream": "^1.0.8", 293 | "mime-types": "^2.1.12" 294 | } 295 | }, 296 | "lodash": { 297 | "version": "4.17.21", 298 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 299 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 300 | }, 301 | "mime-db": { 302 | "version": "1.52.0", 303 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 304 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 305 | }, 306 | "mime-types": { 307 | "version": "2.1.35", 308 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 309 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 310 | "requires": { 311 | "mime-db": "1.52.0" 312 | } 313 | }, 314 | "short-unique-id": { 315 | "version": "4.4.4", 316 | "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz", 317 | "integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==" 318 | }, 319 | "uuid": { 320 | "version": "8.3.2", 321 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 322 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 323 | }, 324 | "uuidv4": { 325 | "version": "6.2.13", 326 | "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", 327 | "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", 328 | "requires": { 329 | "@types/uuid": "8.3.4", 330 | "uuid": "8.3.2" 331 | } 332 | }, 333 | "ws": { 334 | "version": "8.11.0", 335 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 336 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 337 | "requires": {} 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /Examples/FTX-WebSocket-Template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ftx-websocket-template", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "traderjoe155", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@compendiumfi/pendax": "^1.1.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/OKX-WebSocket-Template/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Examples/OKX-WebSocket-Template/README.md: -------------------------------------------------------------------------------- 1 | # OKX Websocket Template built with PENDAX-SDK 2 | https://github.com/CompendiumFi/PENDAX-SDK 3 | 4 | install required packages with ```npm install``` 5 | 6 | 7 | run with ```node index.js``` 8 | 9 | ## This application is a fully functional template for okx websockets utilizing PENDAX-SDK 10 | 11 | Add your api key, secret, and passphrase in index.js under the socket config. adjust parameters as neccesary. If the endpoint you are trying to 12 | use is private, change isPrivate: true. For market data and other public endpoints, isPrivate: false. 13 | 14 | This sample code is using a single okxSubscription, however to add more just declare a new variable and a new socket config. You can refrence more 15 | than one at a time inside the try block in startSocket function. 16 | 17 | Screenshot 2022-11-03 at 4 13 41 AM 18 | 19 | create a subscription inside the doSubscriptions function. 20 | 21 | ex: 22 | ``` 23 | function doSubscriptions(socket) { 24 | subscriptions.tickers = { 25 | name: 'tickers', 26 | args: 27 | [{ channel: 'tickers', instId: 'BTC-USDT-SWAP' }] 28 | } 29 | socket.subscribe(subscriptions.tickers) 30 | console.log('Attempting subscriptions') 31 | } 32 | ``` 33 | 34 | Edit what you want to happen when a message comes in on this channel inside the handleMessage function: 35 | ``` 36 | case 'tickers': 37 | console.log(msg.data) 38 | break; 39 | ``` 40 | ## Result 41 | 42 | Screenshot 2022-11-03 at 4 08 31 AM 43 | 44 | 45 | All websocket functionality available from OKX at time of writing is present in this template 46 | -------------------------------------------------------------------------------- /Examples/OKX-WebSocket-Template/index.js: -------------------------------------------------------------------------------- 1 | import { OkxSocket } from "./node_modules/@compendiumfi/pendax/sockets/okxsocket.js" 2 | // Standalone implementation of OKX sockets 3 | 4 | let okxSocket; 5 | let subscriptions = {} 6 | // Create config object - we will only use this object once - when the socket is originally started 7 | const okxSocketConfig = { 8 | name: 'okxSocket', 9 | key: "", 10 | secret: "", 11 | passphrase: "", 12 | clientReconnect: socketReconnect, 13 | clientOnOpen: okxOnOpen, 14 | clientOnMessage: okxOnMessage, 15 | clientOnError: okxOnError, 16 | clientOnClose: okxOnClose, 17 | pingInterval: 10000, 18 | autoReconnectOnClose: true, 19 | reconnectWaitTime: 3000, 20 | maxRetries: 3, 21 | socket: okxSocket, 22 | isPrivate: false 23 | } 24 | 25 | //Then, for convenience we define a function called startSocket - when called it will take our socketConfig OR the config object from the socket being retried as a parameter 26 | function startSocket(socketConfig) { 27 | // connect 28 | try { 29 | // From this point forward we will access and manage the socket using the returned mySocket object 30 | okxSocket = new OkxSocket(socketConfig) 31 | // setTimeout( 32 | // function (subscriptions, okxSocket) { cancelSubscriptions(subscriptions, okxSocket) }, 30000, subscriptions, okxSocket 33 | // ); 34 | } 35 | catch (error) { 36 | // handle error 37 | } 38 | } 39 | function cancelSubscriptions(subscriptionsToCancel, theSocket) { 40 | for (const subscription in subscriptionsToCancel) { 41 | theSocket.unsubscribe(subscription) 42 | } 43 | } 44 | // Reconnection/retry handler 45 | function socketReconnect(oldSocket) { 46 | // Retrieve the most recent retry number from the old socket 47 | let currentRetry = oldSocket.getRetryNumber(); 48 | // Retrieve the max retries value from the old socket 49 | let maxRetries = oldSocket.getOptions().maxRetries; 50 | // Test to see if we should retry the connection 51 | if (currentRetry <= maxRetries) { 52 | console.log('***************** reconnecting **********************') 53 | console.log('Retry = ', currentRetry) 54 | // Now retrieve the options object from the old socket 55 | const oldSocketOptions = oldSocket.getOptions() 56 | // Finally attempt to restart the socket, passing in oldSocketOptions instead of mySocketConfig as we did the on first startup 57 | startSocket(oldSocketOptions); 58 | } 59 | else { 60 | // We ran out of retries...let the socket die 61 | console.log('max socket reconnect retries exceeded - socket terminated') 62 | } 63 | } 64 | // Event handlers 65 | function okxOnOpen(socketInfo) { 66 | // upon opening we will login (since we are going to subscribe to private data) 67 | // there will be no response in any case 68 | socketInfo.socket.login(); 69 | console.log('Socket opened - attempting login') 70 | } 71 | function placeOrders(socket){ 72 | const orders = { 73 | id: '2637251', 74 | args: [ 75 | { 76 | "side": "buy", 77 | "instId": "BTC-USDT", 78 | "tdMode": "cross", 79 | "ordType": "market", 80 | "sz": "0.01" 81 | } 82 | ] 83 | } 84 | socket.placeOrders(orders) 85 | } 86 | function amendOrders(socket){ 87 | 88 | } 89 | function cancelOrders(socket){ 90 | 91 | } 92 | function doSubscriptions(socket) { 93 | // create our first subscription 94 | // subscriptions.orders = { 95 | // name: 'orders', 96 | // args: 97 | // [{ channel: 'orders'}] 98 | // } 99 | subscriptions.tickers = { 100 | name: 'tickers', 101 | args: 102 | [{ channel: 'tickers', instId: 'BTC-USDT-SWAP' }] 103 | } 104 | // subscriptions.myBalances = { 105 | // name: 'myBalances', 106 | // args: 107 | // [{ channel: 'balance_and_position'}] 108 | // } 109 | // subscriptions.ob = { 110 | // name: 'orderbook', 111 | // args: 112 | // [{ channel: 'books', instId: 'BTC-USDT-SWAP' }] 113 | // } 114 | // subscribe 115 | //socket.subscribe(subscriptions.myOrders) 116 | //socket.subscribe(subscriptions.myBalances) 117 | socket.subscribe(subscriptions.tickers) 118 | //socket.subscribe(subscriptions.orders) 119 | console.log('Attempting subscriptions') 120 | } 121 | function okxOnMessage(socketInfo, msg) { 122 | switch (msg.event) { 123 | case 'login': 124 | console.log('login successful'); 125 | doSubscriptions(socketInfo.socket); 126 | //placeOrders(socketInfo.socket) 127 | break; 128 | case 'error': 129 | if (msg.msg.includes('"op":"login"') || msg.msg.includes('Please log in')) { 130 | socketInfo.socket.loggedOut() 131 | } 132 | console.log('Error code: ', msg.code, ' Message: ', msg.msg) 133 | break; 134 | case 'subscribe': 135 | console.log('Subscribed to channel: ', msg.arg) 136 | break; 137 | case 'unsubscribe': 138 | // test to see if socket has any remaining subscriptions 139 | if (Object.keys(okxSocket.getSubscriptions()).length == 0) { 140 | // if not then kill socket - you must pass the socket instance itself into the kill routine 141 | okxSocket.kill(okxSocket) 142 | } 143 | console.log('Unsubscribed from channel: ', msg.channel) 144 | break; 145 | // case 'info': 146 | // console.log('Info received: Code: ', msg.code, ' Message: ', msg.msg) 147 | // break; 148 | // case 'partial': 149 | // console.log('Market snapshot: ', msg.data) 150 | // break; 151 | // case 'update': 152 | // console.log('Market data: ', msg.data) 153 | // break; 154 | default: 155 | if (msg.event) { 156 | console.log('Unknown event: ', msg.event) 157 | } 158 | else { 159 | handleMessage(msg) 160 | } 161 | break; 162 | } 163 | } 164 | function handleMessage(msg) { 165 | switch (getMessageType(msg)) { 166 | case 'order': 167 | console.log(msg.data) 168 | break; 169 | case 'batch-orders': 170 | console.log(msg.data) 171 | break; 172 | case 'cancel-order': 173 | console.log(msg.data) 174 | break; 175 | case 'batch-cancel-orders': 176 | console.log(msg.data) 177 | break; 178 | case 'amend-order': 179 | console.log(msg.data) 180 | break; 181 | case 'batch-amend-orders': 182 | console.log(msg.data) 183 | break; 184 | case 'account': 185 | console.log(msg.data) 186 | break; 187 | case 'positions': 188 | console.log(msg.data) 189 | break; 190 | case 'balance_and_position': 191 | console.log(msg.data) 192 | break; 193 | case 'orders': 194 | console.log(msg.data) 195 | break; 196 | case 'orders-algo': 197 | console.log(msg.data) 198 | break; 199 | case 'algo-advance': 200 | console.log(msg.data) 201 | break; 202 | case 'liquidation-warning': 203 | console.log(msg.data) 204 | break; 205 | case 'account-greeks': 206 | console.log(msg.data) 207 | break; 208 | case 'rfqs': 209 | console.log(msg.data) 210 | break; 211 | case 'quotes': 212 | console.log(msg.data) 213 | break; 214 | case 'struc-block-trades': 215 | console.log(msg.data) 216 | break; 217 | case 'grid-orders-spot': 218 | console.log(msg.data) 219 | break; 220 | case '"grid-orders-contract': 221 | console.log(msg.data) 222 | break; 223 | case 'grid-positions': 224 | console.log(msg.data) 225 | break; 226 | case 'grid-sub-orders': 227 | console.log(msg.data) 228 | break; 229 | case 'instruments': 230 | console.log(msg.data) 231 | break; 232 | case 'tickers': 233 | console.log(msg.data) 234 | break; 235 | case 'open-interest': 236 | console.log(msg.data) 237 | break; 238 | case 'candle1D': 239 | console.log(msg.data) 240 | break; 241 | case 'trades': 242 | console.log(msg.data) 243 | break; 244 | case 'estimated-price': 245 | console.log(msg.data) 246 | break; 247 | case 'mark-price': 248 | console.log(msg.data) 249 | break; 250 | case 'mark-price-candle1D': 251 | console.log(msg.data) 252 | break; 253 | case 'price-limit': 254 | console.log(msg.data) 255 | break; 256 | case 'books': 257 | console.log(msg.data[0]) 258 | break; 259 | case 'opt-summary': 260 | console.log(msg.data) 261 | break; 262 | case 'funding-rate': 263 | console.log(msg.data) 264 | break; 265 | case 'index-candle1Y': 266 | console.log(msg.data) 267 | break; 268 | case 'index-candle6M': 269 | console.log(msg.data) 270 | break; 271 | case 'index-candle3M': 272 | console.log(msg.data) 273 | break; 274 | case 'index-candle1M': 275 | console.log(msg.data) 276 | break; 277 | case 'index-candle1W': 278 | console.log(msg.data) 279 | break; 280 | case 'index-candle1D': 281 | console.log(msg.data) 282 | break; 283 | case 'index-candle2D': 284 | console.log(msg.data) 285 | break; 286 | case 'index-candle3D': 287 | console.log(msg.data) 288 | break; 289 | case 'index-candle5D': 290 | console.log(msg.data) 291 | break; 292 | case 'index-candle12H': 293 | console.log(msg.data) 294 | break; 295 | case 'index-candle6H': 296 | console.log(msg.data) 297 | break; 298 | case 'index-candle4H': 299 | console.log(msg.data) 300 | break; 301 | case 'index-candle2H': 302 | console.log(msg.data) 303 | break; 304 | case 'index-candle1H': 305 | console.log(msg.data) 306 | break; 307 | case 'index-candle30m': 308 | console.log(msg.data) 309 | break; 310 | case 'index-candle15m': 311 | console.log(msg.data) 312 | break; 313 | case 'index-candle5m': 314 | console.log(msg.data) 315 | break; 316 | case 'index-candle3m': 317 | console.log(msg.data) 318 | break; 319 | case 'index-candle1m': 320 | console.log(msg.data) 321 | break; 322 | case 'index-candle1Yutc': 323 | console.log(msg.data) 324 | break; 325 | case 'index-candle3Mutc': 326 | console.log(msg.data) 327 | break; 328 | case 'index-candle1Mutc': 329 | console.log(msg.data) 330 | break; 331 | case 'index-candle1Wutc': 332 | console.log(msg.data) 333 | break; 334 | case 'index-candle1Dutc': 335 | console.log(msg.data) 336 | break; 337 | case 'index-candle2Dutc': 338 | console.log(msg.data) 339 | break; 340 | case 'index-candle3Dutc': 341 | console.log(msg.data) 342 | break; 343 | case 'index-candle5Dutc': 344 | console.log(msg.data) 345 | break; 346 | case 'index-candle12Hutc': 347 | console.log(msg.data) 348 | break; 349 | case 'index-candle6Hutc': 350 | console.log(msg.data) 351 | break; 352 | case 'index-tickers': 353 | console.log(msg.data) 354 | break; 355 | case 'status': 356 | console.log(msg.data) 357 | break; 358 | case 'public-struc-block-trades': 359 | console.log(msg.data) 360 | break; 361 | case 'block-tickers': 362 | console.log(msg.data) 363 | break; 364 | } 365 | } 366 | function getMessageType(msg) { 367 | if (msg.op) { 368 | return msg.op; 369 | } 370 | else if (msg.arg) { 371 | return msg.arg.channel 372 | } 373 | } 374 | function okxOnError(err, socketInfo) { 375 | console.log(err); 376 | } 377 | function okxOnClose(code, msg, socketInfo) { 378 | // This is where you should end up after 60 seconds 379 | console.log('Code: ', code, ' Message: ', msg); 380 | } 381 | 382 | // start it up 383 | startSocket(okxSocketConfig) 384 | 385 | -------------------------------------------------------------------------------- /Examples/OKX-WebSocket-Template/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "okx-websocket-template", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "okx-websocket-template", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@compendiumfi/pendax": "^1.1.3" 13 | } 14 | }, 15 | "node_modules/@compendiumfi/pendax": { 16 | "version": "1.1.3", 17 | "resolved": "https://registry.npmjs.org/@compendiumfi/pendax/-/pendax-1.1.3.tgz", 18 | "integrity": "sha512-hT5rDYx8yPrnzTudoW6M8JFXxpjZ/5+EsySDcIF3hMrWcnIolGgYtPDv91qahlGqMlYz55jh4AhanDlA79IskA==", 19 | "dependencies": { 20 | "axios": "^0.27.2", 21 | "base64-js": "^1.5.1", 22 | "crypto-js": "^4.1.1", 23 | "dateformat": "^5.0.3", 24 | "dotenv": "^16.0.1", 25 | "lodash": "^4.17.21", 26 | "short-unique-id": "^4.4.4", 27 | "uuidv4": "^6.2.13", 28 | "ws": "^8.7.0" 29 | } 30 | }, 31 | "node_modules/@types/uuid": { 32 | "version": "8.3.4", 33 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", 34 | "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" 35 | }, 36 | "node_modules/asynckit": { 37 | "version": "0.4.0", 38 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 39 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 40 | }, 41 | "node_modules/axios": { 42 | "version": "0.27.2", 43 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", 44 | "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", 45 | "dependencies": { 46 | "follow-redirects": "^1.14.9", 47 | "form-data": "^4.0.0" 48 | } 49 | }, 50 | "node_modules/base64-js": { 51 | "version": "1.5.1", 52 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 53 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 54 | "funding": [ 55 | { 56 | "type": "github", 57 | "url": "https://github.com/sponsors/feross" 58 | }, 59 | { 60 | "type": "patreon", 61 | "url": "https://www.patreon.com/feross" 62 | }, 63 | { 64 | "type": "consulting", 65 | "url": "https://feross.org/support" 66 | } 67 | ] 68 | }, 69 | "node_modules/combined-stream": { 70 | "version": "1.0.8", 71 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 72 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 73 | "dependencies": { 74 | "delayed-stream": "~1.0.0" 75 | }, 76 | "engines": { 77 | "node": ">= 0.8" 78 | } 79 | }, 80 | "node_modules/crypto-js": { 81 | "version": "4.1.1", 82 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 83 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 84 | }, 85 | "node_modules/dateformat": { 86 | "version": "5.0.3", 87 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz", 88 | "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==", 89 | "engines": { 90 | "node": ">=12.20" 91 | } 92 | }, 93 | "node_modules/delayed-stream": { 94 | "version": "1.0.0", 95 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 96 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 97 | "engines": { 98 | "node": ">=0.4.0" 99 | } 100 | }, 101 | "node_modules/dotenv": { 102 | "version": "16.0.3", 103 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 104 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 105 | "engines": { 106 | "node": ">=12" 107 | } 108 | }, 109 | "node_modules/follow-redirects": { 110 | "version": "1.15.2", 111 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 112 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 113 | "funding": [ 114 | { 115 | "type": "individual", 116 | "url": "https://github.com/sponsors/RubenVerborgh" 117 | } 118 | ], 119 | "engines": { 120 | "node": ">=4.0" 121 | }, 122 | "peerDependenciesMeta": { 123 | "debug": { 124 | "optional": true 125 | } 126 | } 127 | }, 128 | "node_modules/form-data": { 129 | "version": "4.0.0", 130 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 131 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 132 | "dependencies": { 133 | "asynckit": "^0.4.0", 134 | "combined-stream": "^1.0.8", 135 | "mime-types": "^2.1.12" 136 | }, 137 | "engines": { 138 | "node": ">= 6" 139 | } 140 | }, 141 | "node_modules/lodash": { 142 | "version": "4.17.21", 143 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 144 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 145 | }, 146 | "node_modules/mime-db": { 147 | "version": "1.52.0", 148 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 149 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 150 | "engines": { 151 | "node": ">= 0.6" 152 | } 153 | }, 154 | "node_modules/mime-types": { 155 | "version": "2.1.35", 156 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 157 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 158 | "dependencies": { 159 | "mime-db": "1.52.0" 160 | }, 161 | "engines": { 162 | "node": ">= 0.6" 163 | } 164 | }, 165 | "node_modules/short-unique-id": { 166 | "version": "4.4.4", 167 | "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz", 168 | "integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==", 169 | "bin": { 170 | "short-unique-id": "bin/short-unique-id", 171 | "suid": "bin/short-unique-id" 172 | } 173 | }, 174 | "node_modules/uuid": { 175 | "version": "8.3.2", 176 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 177 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 178 | "bin": { 179 | "uuid": "dist/bin/uuid" 180 | } 181 | }, 182 | "node_modules/uuidv4": { 183 | "version": "6.2.13", 184 | "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", 185 | "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", 186 | "dependencies": { 187 | "@types/uuid": "8.3.4", 188 | "uuid": "8.3.2" 189 | } 190 | }, 191 | "node_modules/ws": { 192 | "version": "8.11.0", 193 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 194 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 195 | "engines": { 196 | "node": ">=10.0.0" 197 | }, 198 | "peerDependencies": { 199 | "bufferutil": "^4.0.1", 200 | "utf-8-validate": "^5.0.2" 201 | }, 202 | "peerDependenciesMeta": { 203 | "bufferutil": { 204 | "optional": true 205 | }, 206 | "utf-8-validate": { 207 | "optional": true 208 | } 209 | } 210 | } 211 | }, 212 | "dependencies": { 213 | "@compendiumfi/pendax": { 214 | "version": "1.1.3", 215 | "resolved": "https://registry.npmjs.org/@compendiumfi/pendax/-/pendax-1.1.3.tgz", 216 | "integrity": "sha512-hT5rDYx8yPrnzTudoW6M8JFXxpjZ/5+EsySDcIF3hMrWcnIolGgYtPDv91qahlGqMlYz55jh4AhanDlA79IskA==", 217 | "requires": { 218 | "axios": "^0.27.2", 219 | "base64-js": "^1.5.1", 220 | "crypto-js": "^4.1.1", 221 | "dateformat": "^5.0.3", 222 | "dotenv": "^16.0.1", 223 | "lodash": "^4.17.21", 224 | "short-unique-id": "^4.4.4", 225 | "uuidv4": "^6.2.13", 226 | "ws": "^8.7.0" 227 | } 228 | }, 229 | "@types/uuid": { 230 | "version": "8.3.4", 231 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", 232 | "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" 233 | }, 234 | "asynckit": { 235 | "version": "0.4.0", 236 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 237 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 238 | }, 239 | "axios": { 240 | "version": "0.27.2", 241 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", 242 | "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", 243 | "requires": { 244 | "follow-redirects": "^1.14.9", 245 | "form-data": "^4.0.0" 246 | } 247 | }, 248 | "base64-js": { 249 | "version": "1.5.1", 250 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 251 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 252 | }, 253 | "combined-stream": { 254 | "version": "1.0.8", 255 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 256 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 257 | "requires": { 258 | "delayed-stream": "~1.0.0" 259 | } 260 | }, 261 | "crypto-js": { 262 | "version": "4.1.1", 263 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 264 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 265 | }, 266 | "dateformat": { 267 | "version": "5.0.3", 268 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz", 269 | "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==" 270 | }, 271 | "delayed-stream": { 272 | "version": "1.0.0", 273 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 274 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 275 | }, 276 | "dotenv": { 277 | "version": "16.0.3", 278 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 279 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 280 | }, 281 | "follow-redirects": { 282 | "version": "1.15.2", 283 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 284 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" 285 | }, 286 | "form-data": { 287 | "version": "4.0.0", 288 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 289 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 290 | "requires": { 291 | "asynckit": "^0.4.0", 292 | "combined-stream": "^1.0.8", 293 | "mime-types": "^2.1.12" 294 | } 295 | }, 296 | "lodash": { 297 | "version": "4.17.21", 298 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 299 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 300 | }, 301 | "mime-db": { 302 | "version": "1.52.0", 303 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 304 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 305 | }, 306 | "mime-types": { 307 | "version": "2.1.35", 308 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 309 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 310 | "requires": { 311 | "mime-db": "1.52.0" 312 | } 313 | }, 314 | "short-unique-id": { 315 | "version": "4.4.4", 316 | "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz", 317 | "integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==" 318 | }, 319 | "uuid": { 320 | "version": "8.3.2", 321 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 322 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 323 | }, 324 | "uuidv4": { 325 | "version": "6.2.13", 326 | "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", 327 | "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", 328 | "requires": { 329 | "@types/uuid": "8.3.4", 330 | "uuid": "8.3.2" 331 | } 332 | }, 333 | "ws": { 334 | "version": "8.11.0", 335 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 336 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 337 | "requires": {} 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /Examples/OKX-WebSocket-Template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "okx-websocket-template", 3 | "version": "1.0.0", 4 | "description": "https://github.com/CompendiumFi/PENDAX-SDK", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "traderjoe155", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@compendiumfi/pendax": "^1.1.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PENDAX SOFTWARE DEVELOPMENT KIT (SDK) 2 | 3 | [![npm](https://img.shields.io/npm/v/@compendiumfi/pendax.svg)](https://www.npmjs.com/package/@compendiumfi/pendax) ![Discord](https://img.shields.io/discord/846967032288509953?label=Discord) ![GitHub Repo stars](https://img.shields.io/github/stars/CompendiumFi/PENDAX-SDK?style=social) 4 | 5 | Javascript SDK for trading on cryptocurrency exchanges like OKX, Bybit, Bitget, MEXC, and more. Supports API data feeds and WebSocket. Built and maintained by the team at [Compendium](https://compendium.finance). 6 | 7 | ### [Installation](#installation) · [Features](#features) · [Documentation](#documentation) · [Examples](https://github.com/CompendiumFi/PENDAX-SDK/tree/main/Examples) · [Compendium](https://compendium.finance) · [Social](#social) 8 | 9 | PENDAX provides simplified exchange commands and interoperability. Our underlying high-frequency trading engine, PENDAX, is available for all developers, traders, financial analysts, and data scientists to build custom integrations on. It provides simple access to a variety of different use cases revolving around trading and data deployments. 10 | 11 | Visit The [Official PENDAX Website](https://pendax.pro). 12 | ## Installation 13 | 14 | Install The PENDAX SDK 15 | 16 | ```bash 17 | npm i @compendiumfi/pendax 18 | ``` 19 | 20 | ## Current Supported Exchange Platforms 21 | 22 | | Logo | Exchange | PENDAX ID | Integration Status | Completion Level | Exchange Functions | 23 | | ---- | -------- | --------- | ------------------ | ---------------- | ------------------ | 24 | | ![OKX-PENDAX-TRADING-DEVELOPER](https://user-images.githubusercontent.com/36686278/199347395-cfa4ada1-64a9-4e19-a4d5-1821d4a0c0ac.png) | [OKX (Formerly OKex)](https://www.okx.com/join/COMPENDIUM) | okx | 🟢 Integrated | 🟢🟢🟢🟢🟢 | [View Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk/okx-functions) | 25 | | ![BitGet-PENDAX-TRADING-DEVELOPER](https://user-images.githubusercontent.com/36686278/205699916-169bf2eb-c731-44cb-b4ac-5e44a8b9b932.png) | [Bitget](https://partner.bitget.com/bg/gacm69031670266129840) | bitget | 🟢 Integrated | 🟢🟢🟢🟢🟢 | [View Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk/bitget-functions) | 26 | | ![Blofin-PENDAX-TRADING-DEVELOPER](https://github.com/CompendiumFi/PENDAX-SDK/assets/36686278/3b0cd253-448b-40b1-9716-2db26c9cac2a) | [Blofin](https://partner.blofin.com/d/pendax) | blofin | 🟢 Integrated | 🟢🟢🟢🟢🟢 | [View Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk/blofin-functions) | 27 | | ![Bybit-PENDAX-TRADING-DEVELOPER](https://user-images.githubusercontent.com/36686278/199347652-d196703c-103d-4060-b23d-7f4534e03057.png) | [ByBit](https://www.bybit.com/en-US/invite?ref=LPMYYV) | bybit | 🟢 Integrated | 🟢🟢🟢🟢🟢 | [View Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk/bybit-functions) | 28 | | ![MEXC-PENDAX-TRADING-DEVELOPER](https://github.com/CompendiumFi/PENDAX-SDK/assets/36686278/8b42b17c-d331-4f34-9d7d-364d8dc74cae) | [MEXC](https://www.mexc.com/register?inviteCode=1QRLp) | mexc | 🟢 Integrated | 🟢🟢🟢🟢🟢 | [View Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk/mexc-functions) | 29 | | ![phemex-PENDAX-TRADING-DEVELOPER](https://github.com/CompendiumFi/PENDAX-SDK/assets/36686278/3afd262e-5baf-441e-b784-bcc1d8206a1a) | [Phemex](https://phemex.com/account/referral/invite-friends-entry?referralCode=JWAPJ5) | phemex | 🟢 Integrated | 🟢🟢🟢🟢🟢 | [View Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk/phemex-functions) | 30 | 31 | 32 | 33 | 34 | ### Integration Roadmap Includes: 35 | Binance, Binance US, BitMex, Coinbase Pro, Crypto.com, Gate.io, HTX, Kraken, Kucoin, & More. 36 | 37 | 38 | ## Features 39 | 40 | For a complete capability and usage guidelines list please visit [PENDAX Usage Capabilities](https://docs.compendium.finance/pendax/pendax-capabilities). 41 | - Full Public and Private HTTP REST APIs for all supported platforms are integrated in an efficient manner for secondary deployment. 42 | - Aggregated commands are normalized between systems in the unified package making PENDAX extremely easy to integrate. 43 | - WebSocket and live data capabilities are included for all supported exchanges. 44 | - No subscription is needed for a "Professional" version of PENDAX. All commands and capabilities are free to use for all developers, traders, and integrators. 45 | 46 | PENDAX consists of both public and private integrations. A public integration can be accessed immediately after integration and provides unrestricted access to data for all supported exchange markets and integrations without an API key. Private integrations feature the ability to interact with an account or post trades and require authorization methods in order to complete command tasks. Users will need to create an API key on their selected exchange. Documentation within the Connect An Exchange Account segment can help you find how to create keys for each integrated exchange. 47 | ### Public API Capabilities 48 | Public APIs provide immediate and unrestricted access to the following commands without the need for authorization. 49 | - Fetch all market pairs from each integrated platform and exchange 50 | - Fetch tickers and trading parameters for all market pairs 51 | - Live and historical price feeds and exchange rates for each market pair 52 | - Live order book data for each market pair 53 | - Fetch trade history for all market pairs 54 | - Live and historical charting capabilities utilizing trading data 55 | - Other supported public endpoints for individual exchanges outside of this list 56 | > Capabilities may vary depending on the specific exchange integrations and their public access data feeds. Each exchange also has its own rate limiting guidelines that each user will be required to follow. 57 | 58 | ### Private API Capabilities 59 | Authorized commands provide the ability to interact with trade orders, account data, and more. As mentioned before you will need to generate an API key from the selected exchange and then insert the authorization parameters into PENDAX to access these commands. 60 | - Fetch and manage personal account information 61 | - Create and edit subaccounts associated with the main account on supported exchanges 62 | - Query account and subaccount balances 63 | - Ability to deposit and withdraw from accounts and subaccounts 64 | - Query current orders and historical personal orders 65 | - Fetch personal ledger history for accounts and subaccounts 66 | - Transfer funds between exchanges, accounts, and subaccounts 67 | - Trade with all supported order types on supported exchanges 68 | - Trade on all supported markets on supported exchanges 69 | - Open and manage WebSockets on supported exchanges 70 | > Capabilities may vary depending on the specific exchange integrations and their documented capabilities for authorized API calls. Each exchange also has its own rate limiting guidelines that each user will be required to follow. 71 | 72 | ### Example Use Cases 73 | Example programs posted by the Compendium team can be found in this Github Repo on the [Examples](https://github.com/CompendiumFi/PENDAX-SDK/tree/main/Examples) page. Other programs and use cases that can be built with PENDAX include the following: 74 | - Aggregated order books and optimal trade routing 75 | - Fully customized private trading bot strategy 76 | - Dollar-Cost Averaging strategies 77 | - Cryptocurrency arbitrage strategies 78 | - Grid-Trading Strategies 79 | - Market Making for Cryptocurrency exchange markets 80 | - Customized graphical interfaces for trading 81 | - New market or statistics notifications 82 | - Discord, Telegram, & Slack bots (or any other platform!) 83 | - Custom hardware macro development (Streamdeck, etc) 84 | - Compiling custom market charts 85 | - Portfolio management, Trade journals, and PnL% tracking 86 | - Data visualization and analytics interface 87 | - Custom low-latency command-line interfaces (CLI) 88 | - Advanced trading strategy indicators 89 | - Tax Reporting Software 90 | 91 | ## Documentation 92 | 93 | Full instructions for utilizing the PENDAX SDK and tooling can be found by visiting the [Documentation](https://docs.compendium.finance/pendax/using-pendax-sdk). 94 | 95 | Functions within the PENDAX SDK are separate into two main core groups: "Common Functions" and "Exchange Specific Functions". 96 | 97 | ### Common Functions 98 | Our team has done an excellent job in aggregating what we will refer to as "Common Functions" for a plethora of integrated platforms. Common functions are widely used calls available on different platforms and will work by just changing the "exchange" parameter and making edits to API credential layouts (depending on the selected exchange). 99 | Examples of common functions include calls like placing trade orders or getting account balances. 100 | 101 | By normalizing these commands developers can work in a more efficient manner while customizing integrations or code bases for multiple or different supported platforms. 102 | 103 | Documentation relating to "Common Functions" can be found [here](https://docs.compendium.finance/pendax/using-pendax-sdk). 104 | 105 | ### Exchange Specific Functions 106 | While there are a large variety of commonly normalized functions, there also may be a number of exchange-specific calls available for your selected platform. Many exchanges may include API features native to their interface and not found on other platforms so designating a common function is difficult in this manner. 107 | We recommend browsing both the Common Functions and exchange-specific documentation pages while creating any instrument with PENDAX to ensure your code is designed to interact with the SDK in the correct manner. 108 | 109 | To explore "Exchange Specific Functions" please visit the [Using The PENDAX SDK](https://docs.compendium.finance/pendax/using-pendax-sdk) page and select the supported exchange of your choice. 110 | 111 | ## FAQ 112 | 113 | #### Do I Need Coding Knowledge To Use This Product? 114 | 115 | Current PENDAX deployments are optimized for traders familiar with coding so we would recommend ample experience to utilize the full potential of our SDK and libraries. We have however focused on simplifying commands and other functions in comparison to comparable libraries so traders with less coding experience may find PENDAX easier to comprehend. 116 | 117 | #### What Coding Languages is PENDAX currently available for? 118 | 119 | PENDAX is currently available as a Javascript library and SDK. Hosted REST APIs will be available shortly for users more familiar with integrating traditional API setups. 120 | 121 | #### Are All Facets Of The PENDAX SDK Free To Use? 122 | 123 | Yes, the full PENDAX SDK is completely free to use for all developers, traders, analysts and more in accordance with our licensing and guidelines. 124 | 125 | #### Where Can I See The PENDAX SDK In Action Or Find Coding Examples? 126 | 127 | The best way to showcase capabilities is to build on PENDAX. We've released the new version of Compendium utilizing the latest PENDAX SDK capabilities. Our team will also add examples and code snippets to this repository to help speed up development. 128 | 129 | #### Can I Request New Exchange Or Additions To The SDK? 130 | 131 | The PENDAX tool suite is maintained by the team at Compendium. We are always looking for new community requests and additions that users would find useful to add in the next iterations. Join our Discord or email us at support@compendium.finance. 132 | 133 | 134 | ## Support & Contact Information 135 | 136 | The PENDAX SDK/API's are built and maintained by the team at Compendium (Formerly branded as Compendium.Fi / Compendium Finance). Developers can use one of the following official channels for support. 137 | 138 | - Discord: https://discord.gg/64r2xtqczs 139 | - Telegram: https://t.me/CompendiumFinanceOfficial 140 | - Email: Support@Compendium.Finance 141 | 142 | ### Social 143 | 144 | - Compendium Twitter: https://twitter.com/CompendiumFi 145 | - Pendax Twitter: https://twitter.com/PendaxPro 146 | - Youtube Channel: https://www.youtube.com/channel/UC749uyBnzwgTegYa0LkSj5w 147 | 148 | 149 | 150 | ## License Agreement 151 | 152 | [Click Here To View The License Agreement.](https://docs.compendium.finance/pendax/license-agreement) This Agreement becomes effective as of the date you first access, download, or use the API or SDK. This Agreement shall continue until terminated either by us or by you. Even after termination of this Agreement, certain provisions will survive, as discussed herein. This Agreement also incorporates Compendium's Terms of Service and Privacy Policy which terms shall also govern your use of the Service. 153 | --------------------------------------------------------------------------------