├── .gitignore ├── Ideas ├── DOW30data.csv └── logit.ipynb ├── LICENSE ├── Legacy ├── LICENSE ├── Logit Data.ipynb ├── README.md ├── account.py ├── approx_entropy.py ├── buy_close_sell_open.py ├── cancel_pending_order.py ├── combo_order.py ├── condor_ex.py ├── config.py ├── data.py ├── demo.py ├── equity_orders.py ├── event.py ├── get_account_balance.py ├── get_cancelled_orders.py ├── get_clock.py ├── get_gainloss.py ├── get_historical_quotes.py ├── get_market_calendar.py ├── get_option_symbols.py ├── get_orders.py ├── get_positions.py ├── get_quote.py ├── get_timesales.py ├── getoptionchain.py ├── getoptionexpiry.py ├── getoptionstrike.py ├── getuser.py ├── idk.py ├── logit_data.py ├── main.py ├── married_put.py ├── option_orders.py ├── placeorder.py ├── plot_timesales_gpt.py ├── price_per_share.py ├── samp_entropy.py ├── sp500_data.py ├── straddle_orders.py ├── testrun_will.ipynb ├── tradier.py ├── tradier_examples.py ├── tradierinfo.txt ├── tradiersetup.txt ├── uvatradier │ ├── LICENSE │ ├── README.md │ ├── build │ │ └── lib │ │ │ └── uvatradier │ │ │ ├── __init__.py │ │ │ ├── account.py │ │ │ ├── base.py │ │ │ ├── equity_order.py │ │ │ ├── options_data.py │ │ │ ├── options_order.py │ │ │ ├── quotes.py │ │ │ └── tradier.py │ ├── dist │ │ ├── uvatradier-0.0.2-py3-none-any.whl │ │ ├── uvatradier-0.0.2.tar.gz │ │ ├── uvatradier-0.0.3-py3-none-any.whl │ │ ├── uvatradier-0.0.3.tar.gz │ │ ├── uvatradier-0.0.4-py3-none-any.whl │ │ └── uvatradier-0.0.4.tar.gz │ ├── examples │ │ └── account_ex.py │ ├── setup.py │ ├── uvatradier.egg-info │ │ ├── PKG-INFO │ │ ├── SOURCES.txt │ │ ├── dependency_links.txt │ │ └── top_level.txt │ └── uvatradier │ │ ├── __init__.py │ │ ├── account.py │ │ ├── base.py │ │ ├── equity_order.py │ │ ├── options_data.py │ │ ├── options_order.py │ │ ├── quotes.py │ │ └── tradier.py └── yahoo.py ├── README.md ├── examples ├── account_ex.py ├── demo.py └── quotes_ex.py ├── setup.py └── uvatradier ├── __init__.py ├── account.py ├── base.py ├── equity_order.py ├── options_data.py ├── options_order.py ├── quotes.py ├── stream.py └── tradier.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | .ipynb_checkpoints/ 4 | __pycache__/ 5 | uvatradier/.DS_Store 6 | 7 | dist/ 8 | build/ 9 | *.egg-info/ 10 | 11 | -------------------------------------------------------------------------------- /Ideas/logit.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "7a990f53", 7 | "metadata": { 8 | "tags": [] 9 | }, 10 | "outputs": [ 11 | { 12 | "name": "stdout", 13 | "output_type": "stream", 14 | "text": [ 15 | "hello, world!\n", 16 | " date open high low close volume\n", 17 | "0 2022-10-03 56.36 56.850 56.060 56.65 16277620\n", 18 | "1 2022-10-04 56.95 57.810 56.460 56.78 18861431\n", 19 | "2 2022-10-05 56.50 56.650 55.510 56.24 14324851\n", 20 | "3 2022-10-06 56.00 56.100 54.870 55.03 17192843\n", 21 | "4 2022-10-07 54.99 55.090 54.250 54.51 11479799\n", 22 | "... ... ... ... ... ... ...\n", 23 | "7495 2023-09-25 72.40 73.630 72.400 73.36 1790625\n", 24 | "7496 2023-09-26 72.70 73.550 72.360 72.78 2517575\n", 25 | "7497 2023-09-27 73.14 74.010 72.830 73.47 2018135\n", 26 | "7498 2023-09-28 73.46 74.855 73.460 74.54 2122866\n", 27 | "7499 2023-09-29 75.11 75.460 74.465 74.59 2315988\n", 28 | "\n", 29 | "[7500 rows x 6 columns]\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "#\n", 35 | "# Example - Using uvatradier to fetch quote data for a given symbol\n", 36 | "#\n", 37 | "\n", 38 | "import os, dotenv; import pandas as pd;\n", 39 | "from uvatradier import Tradier, Quotes\n", 40 | "\n", 41 | "# Create .env file within current working directory.\n", 42 | "# Add the following to your .env file. Specify the correct account number and authorization token for your quote.\n", 43 | "# \ttradier_acct = \n", 44 | "#\ttradier_token = \n", 45 | "\n", 46 | "dotenv.load_dotenv()\n", 47 | "\n", 48 | "ACCOUNT_NUMBER = os.getenv('tradier_acct')\n", 49 | "AUTH_TOKEN = os.getenv('tradier_token')\n", 50 | "\n", 51 | "# Initializing new Quotes object\n", 52 | "quotes = Quotes(ACCOUNT_NUMBER, AUTH_TOKEN);\n", 53 | "\n", 54 | "DOW_30_TICKER = [\n", 55 | " \"AAPL\",\n", 56 | " \"MSFT\",\n", 57 | " \"JPM\",\n", 58 | " \"V\",\n", 59 | " \"RTX\",\n", 60 | " \"PG\",\n", 61 | " \"GS\",\n", 62 | " \"NKE\",\n", 63 | " \"DIS\",\n", 64 | " \"AXP\",\n", 65 | " \"HD\",\n", 66 | " \"INTC\",\n", 67 | " \"WMT\",\n", 68 | " \"IBM\",\n", 69 | " \"MRK\",\n", 70 | " \"UNH\",\n", 71 | " #\"KO\",\n", 72 | " \"CAT\",\n", 73 | " \"TRV\",\n", 74 | " \"JNJ\",\n", 75 | " \"CVX\",\n", 76 | " \"MCD\",\n", 77 | " \"VZ\",\n", 78 | " \"CSCO\",\n", 79 | " \"XOM\",\n", 80 | " \"BA\",\n", 81 | " \"MMM\",\n", 82 | " \"PFE\",\n", 83 | " \"WBA\",\n", 84 | " \"DD\",\n", 85 | "]\n", 86 | "\n", 87 | "# data = pd.DataFrame(quotes.get_historical_quotes('KO', start_date='2022-10-01', end_date='2023-10-01'))\n", 88 | "\n", 89 | "# for symb in DOW_30_TICKER:\n", 90 | "# data_to_append = pd.DataFrame(quotes.get_historical_quotes(symb, start_date='2022-10-01', end_date='2023-10-01'))\n", 91 | "# data = pd.concat([data, data_to_append], ignore_index=True)\n", 92 | "\n", 93 | "data = pd.read_csv(\"DOW30data.csv\")\n", 94 | "\n", 95 | "print(data);\n", 96 | "\n", 97 | "# data['close_change'] = data['close'].pct_change()\n", 98 | "# data['volume_change'] = data['volume'].pct_change()\\\n", 99 | "close_change = data['close'].pct_change();\n", 100 | "volume_change = data['volume'].pct_change();" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 2, 106 | "id": "3da6c557-e86f-4b0d-ac83-caf6b26847ad", 107 | "metadata": { 108 | "tags": [] 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "# import csv\n", 113 | "\n", 114 | "# # Specify the file name\n", 115 | "# file_name = 'DOW30data.csv'\n", 116 | "\n", 117 | "# # Write the DataFrame to a CSV file\n", 118 | "# data.to_csv(file_name, index=False)\n", 119 | "\n", 120 | "# print(f'Data has been written to {file_name}')" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 3, 126 | "id": "7aa86eb6", 127 | "metadata": { 128 | "tags": [] 129 | }, 130 | "outputs": [ 131 | { 132 | "name": "stdout", 133 | "output_type": "stream", 134 | "text": [ 135 | "0 NaN\n", 136 | "1 0.002295\n", 137 | "2 -0.009510\n", 138 | "3 -0.021515\n", 139 | "4 -0.009449\n", 140 | " ... \n", 141 | "7495 0.007139\n", 142 | "7496 -0.007906\n", 143 | "7497 0.009481\n", 144 | "7498 0.014564\n", 145 | "7499 0.000671\n", 146 | "Name: close, Length: 7500, dtype: float64\n" 147 | ] 148 | } 149 | ], 150 | "source": [ 151 | "print(close_change)" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 4, 157 | "id": "1f8c30e6", 158 | "metadata": { 159 | "tags": [] 160 | }, 161 | "outputs": [ 162 | { 163 | "data": { 164 | "text/plain": [ 165 | "0 NaN\n", 166 | "1 0.158734\n", 167 | "2 -0.240522\n", 168 | "3 0.200211\n", 169 | "4 -0.332292\n", 170 | " ... \n", 171 | "7495 -0.074943\n", 172 | "7496 0.405976\n", 173 | "7497 -0.198381\n", 174 | "7498 0.051895\n", 175 | "7499 0.090972\n", 176 | "Name: volume, Length: 7500, dtype: float64" 177 | ] 178 | }, 179 | "execution_count": 4, 180 | "metadata": {}, 181 | "output_type": "execute_result" 182 | } 183 | ], 184 | "source": [ 185 | "volume_change" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 10, 191 | "id": "5a01ff11", 192 | "metadata": { 193 | "tags": [] 194 | }, 195 | "outputs": [ 196 | { 197 | "name": "stdout", 198 | "output_type": "stream", 199 | "text": [ 200 | " close volume\n", 201 | "0 0.001657 36.436996\n", 202 | "1 0.002295 0.158734\n", 203 | "2 -0.009510 -0.240522\n", 204 | "3 -0.021515 0.200211\n", 205 | "4 -0.009449 -0.332292\n", 206 | "... ... ...\n", 207 | "7495 0.007139 -0.074943\n", 208 | "7496 -0.007906 0.405976\n", 209 | "7497 0.009481 -0.198381\n", 210 | "7498 0.014564 0.051895\n", 211 | "7499 0.000671 0.090972\n", 212 | "\n", 213 | "[7500 rows x 2 columns]\n" 214 | ] 215 | } 216 | ], 217 | "source": [ 218 | "# Given the close_change Series (as per your example)\n", 219 | "# close_change = pd.Series([None, 0.002295, -0.009510, -0.021515, -0.009449], name='close') # truncated for brevity\n", 220 | "\n", 221 | "# Replace NaN values with the mean of the non-NaN values\n", 222 | "close_change_filled = close_change.fillna(close_change.mean())\n", 223 | "volume_change_filled = volume_change.fillna(volume_change.mean())\n", 224 | "\n", 225 | "# print(close_change_filled)\n", 226 | "# print(volume_change_filled)\n", 227 | "\n", 228 | "\n", 229 | "logit_data = pd.DataFrame({'close':close_change_filled, 'volume':volume_change_filled});\n", 230 | "print(logit_data)\n" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 14, 236 | "id": "6bff4ce6-03f1-43d8-9ae1-8ace58a02bcd", 237 | "metadata": { 238 | "tags": [] 239 | }, 240 | "outputs": [ 241 | { 242 | "data": { 243 | "text/html": [ 244 | "
\n", 245 | "\n", 258 | "\n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | "
closevolume
00.00165736.436996
486-0.0186029333.799298
736-0.01434953227.733333
2000-0.73606912.611002
3000-0.91074311.589635
32360.00413469157.205000
5986-0.007218139159.880000
72360.0130601806.040041
\n", 309 | "
" 310 | ], 311 | "text/plain": [ 312 | " close volume\n", 313 | "0 0.001657 36.436996\n", 314 | "486 -0.018602 9333.799298\n", 315 | "736 -0.014349 53227.733333\n", 316 | "2000 -0.736069 12.611002\n", 317 | "3000 -0.910743 11.589635\n", 318 | "3236 0.004134 69157.205000\n", 319 | "5986 -0.007218 139159.880000\n", 320 | "7236 0.013060 1806.040041" 321 | ] 322 | }, 323 | "execution_count": 14, 324 | "metadata": {}, 325 | "output_type": "execute_result" 326 | } 327 | ], 328 | "source": [ 329 | "logit_data.query(\"(volume > 10) or (volume < -10)\")" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 6, 335 | "id": "85b71d0a", 336 | "metadata": { 337 | "tags": [] 338 | }, 339 | "outputs": [ 340 | { 341 | "name": "stdout", 342 | "output_type": "stream", 343 | "text": [ 344 | "Model Accuracy: 52.40%\n" 345 | ] 346 | } 347 | ], 348 | "source": [ 349 | "import pandas as pd\n", 350 | "from sklearn.linear_model import LogisticRegression\n", 351 | "from sklearn.model_selection import train_test_split\n", 352 | "\n", 353 | "# # Assuming these are your Series (based on provided data)\n", 354 | "# close = pd.Series([\n", 355 | "# -0.000009, 0.002295, -0.009510, -0.021515, -0.009449, # truncated for brevity\n", 356 | "# -0.010417, -0.008246, -0.010260, -0.002502, 0.003046\n", 357 | "# ])\n", 358 | "\n", 359 | "# volume = pd.Series([\n", 360 | "# 0.057599, 0.158734, -0.240522, 0.200211, -0.332292, # truncated for brevity\n", 361 | "# -0.156129, -0.094194, 0.229716, -0.093092, -0.017077\n", 362 | "# ])\n", 363 | "\n", 364 | "# Convert close to binary. Assuming a positive change is 1 and non-positive is 0.\n", 365 | "logit_data['close_binary'] = (logit_data['close'] > 0).astype(int)\n", 366 | "\n", 367 | "# Split data into training and testing sets\n", 368 | "X_train, X_test, y_train, y_test = train_test_split(logit_data[['volume']], logit_data['close_binary'], test_size=0.2, random_state=42)\n", 369 | "\n", 370 | "# Create a logistic regression model\n", 371 | "model = LogisticRegression()\n", 372 | "\n", 373 | "# Fit the model\n", 374 | "model.fit(X_train, y_train)\n", 375 | "\n", 376 | "# Get the score of the model\n", 377 | "score = model.score(X_test, y_test)\n", 378 | "\n", 379 | "print(f\"Model Accuracy: {score*100:.2f}%\")\n" 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": null, 385 | "id": "a376893e", 386 | "metadata": {}, 387 | "outputs": [], 388 | "source": [] 389 | } 390 | ], 391 | "metadata": { 392 | "kernelspec": { 393 | "display_name": "Python 3 (ipykernel)", 394 | "language": "python", 395 | "name": "python3" 396 | }, 397 | "language_info": { 398 | "codemirror_mode": { 399 | "name": "ipython", 400 | "version": 3 401 | }, 402 | "file_extension": ".py", 403 | "mimetype": "text/x-python", 404 | "name": "python", 405 | "nbconvert_exporter": "python", 406 | "pygments_lexer": "ipython3", 407 | "version": "3.11.5" 408 | } 409 | }, 410 | "nbformat": 4, 411 | "nbformat_minor": 5 412 | } 413 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Legacy/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Legacy/README.md: -------------------------------------------------------------------------------- 1 | # tradier 2 | 3 | Python wrapper for the tradier brokerage API 4 | 5 | Class based functionality currently in development. Code is found in tradier.py 6 | 7 | 8 | To get tradier.py to work, need to have 'tradier_acct' and 'tradier_token' defined in a .env file in the same directory. 9 | 10 | From a python file / jupyter notebook, just run `from tradier import *` 11 | -------------------------------------------------------------------------------- /Legacy/account.py: -------------------------------------------------------------------------------- 1 | from tradier import Tradier; 2 | 3 | class Account (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Account endpoints 9 | # 10 | 11 | self.PROFILE_ENDPOINT = "v1/user/profile"; # GET 12 | 13 | self.POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(ACCOUNT_NUMBER); # GET 14 | 15 | 16 | self.ACCOUNT_BALANCE_ENDPOINT = "v1/accounts/{}/balances".format(ACCOUNT_NUMBER); # GET 17 | self.ACCOUNT_GAINLOSS_ENDPOINT = "v1/accounts/{}/gainloss".format(ACCOUNT_NUMBER); # GET 18 | self.ACCOUNT_HISTORY_ENDPOINT = "v1/accounts/{}/history".format(ACCOUNT_NUMBER); # GET 19 | self.ACCOUNT_POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(ACCOUNT_NUMBER); # GET 20 | 21 | self.ACCOUNT_INDIVIDUAL_ORDER_ENDPOINT = "v1/accounts/{account_id}/orders/{order_id}"; # GET 22 | 23 | 24 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(ACCOUNT_NUMBER); # GET 25 | 26 | def get_user_profile(self): 27 | ''' 28 | Fetch the user profile information from the Tradier Account API. 29 | 30 | This function makes a GET request to the Tradier Account API to retrieve the user profile 31 | information associated with the trading account linked to the provided credentials. 32 | The API response is expected to be in JSON format, containing details about the user profile. 33 | 34 | Returns: 35 | pandas.DataFrame: A DataFrame containing user profile information. 36 | 37 | Example: 38 | # Retrieve user profile information 39 | user_profile = get_user_profile() 40 | transposed_profile = user_profile.T # Transpose the DataFrame for easy viewing. 41 | 42 | Example DataFrame (transposed): 43 | 44 | id id-sb-2r01lpprbg 45 | name Fat Albert 46 | account.account_number ABC1234567 47 | account.classification individual 48 | account.date_created 2021-06-23T22:04:20.000Z 49 | account.day_trader False 50 | account.option_level 6 51 | account.status active 52 | account.type margin 53 | account.last_update_date 2021-06-23T22:04:20.000Z 54 | ''' 55 | r = requests.get( 56 | url = '{}/{}'.format(self.SANDBOX_URL, self.PROFILE_ENDPOINT), 57 | params = {}, 58 | headers = self.REQUESTS_HEADERS 59 | ); 60 | 61 | return pd.json_normalize(r.json()['profile']); 62 | 63 | 64 | def get_account_balance(self): 65 | ''' 66 | Fetch the account balance information from the Tradier Account API. 67 | 68 | This function makes a GET request to the Tradier Account API to retrieve the account 69 | balance information for the trading account associated with the provided credentials. 70 | The API response is expected to be in JSON format, containing details about the account 71 | balance. 72 | 73 | Returns: 74 | pandas.DataFrame: A DataFrame containing account balance information. 75 | 76 | Example: 77 | # Retrieve account balance information 78 | account_balance = get_account_balance() 79 | transposed_balance = account_balance.T # Transpose the DataFrame for easy viewing. 80 | 81 | Example DataFrame (transposed): 82 | 0 83 | option_short_value -147.0 84 | total_equity 74314.82 85 | account_number ABC1234567 86 | account_type margin 87 | close_pl 0 88 | current_requirement 17595.08 89 | equity 0 90 | long_market_value 34244.16 91 | market_value 34097.16 92 | open_pl -225300.265 93 | option_long_value 1054.0 94 | option_requirement 1000.0 95 | pending_orders_count 8 96 | short_market_value -147.0 97 | stock_long_value 33190.16 98 | total_cash 40217.66 99 | uncleared_funds 0 100 | pending_cash 16892.9 101 | margin.fed_call 0 102 | margin.maintenance_call 0 103 | margin.option_buying_power 38919.84 104 | margin.stock_buying_power 77839.68 105 | margin.stock_short_value 0 106 | margin.sweep 0 107 | ''' 108 | r = requests.get( 109 | url = '{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_BALANCE_ENDPOINT), 110 | params = {}, 111 | headers = self.REQUESTS_HEADERS 112 | ); 113 | 114 | return pd.json_normalize(r.json()['balances']); 115 | 116 | 117 | def get_gainloss (self): 118 | ''' 119 | Get cost basis information for a specific user account. 120 | This includes information for all closed positions. 121 | Cost basis information is updated through a nightly batch reconciliation process with tradier clearing firm. 122 | 123 | Returns: 124 | Pandas dataframe with columns [close_date, cost, gain_loss, gain_loss_percent, open_date, proceeds, quantity, symbol, term] 125 | 126 | Example: 127 | >>> account.get_gainloss().head() 128 | close_date cost gain_loss gain_loss_percent open_date proceeds quantity symbol term 129 | 0 2023-09-13T00:00:00.000Z 194700.0 -30600.0 -15.72 2023-08-25T00:00:00.000Z 164100.0 10.0 LMT240119C00260000 19 130 | 1 2023-09-13T00:00:00.000Z 10212.2 -432.6 -4.24 2023-09-06T00:00:00.000Z 9779.6 20.0 KLAC 7 131 | 2 2023-09-13T00:00:00.000Z 2300.0 175.0 7.61 2023-08-24T00:00:00.000Z 2475.0 1.0 HAL251219C00018000 20 132 | 3 2023-09-13T00:00:00.000Z 20700.0 1620.0 7.83 2023-08-24T00:00:00.000Z 22320.0 9.0 HAL251219C00018000 20 133 | 4 2023-09-06T00:00:00.000Z 16967.0 -193.0 -1.14 2023-09-01T00:00:00.000Z 16774.0 100.0 TXN 5 134 | ''' 135 | r = requests.get( 136 | url = '{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_GAINLOSS_ENDPOINT), 137 | params = {}, 138 | headers = self.REQUESTS_HEADERS 139 | ); 140 | 141 | return pd.json_normalize(r.json()['gainloss']['closed_position']); 142 | 143 | 144 | def get_orders (self): 145 | ''' 146 | This function returns a pandas DataFrame. 147 | Each row denotes a queued order. Each column contiains a feature_variable pertaining to the order. 148 | Transposed sample output has the following structure: 149 | 150 | >>> account.get_orders().T 151 | 0 1 152 | id 8248093 8255194 153 | type stop_limit market 154 | symbol UNP CF 155 | side buy buy 156 | quantity 3.0 10.0 157 | status open filled 158 | duration day gtc 159 | price 200.0 NaN 160 | avg_fill_price 0.0 87.39 161 | exec_quantity 0.0 10.0 162 | last_fill_price 0.0 87.39 163 | last_fill_quantity 0.0 10.0 164 | remaining_quantity 3.0 0.0 165 | stop_price 200.0 NaN 166 | create_date 2023-09-25T20:29:10.351Z 2023-09-26T14:45:00.155Z 167 | transaction_date 2023-09-26T12:30:19.152Z 2023-09-26T14:45:00.216Z 168 | class equity equity 169 | ''' 170 | 171 | r = requests.get( 172 | url='{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 173 | params={'includeTags':'true'}, 174 | headers=self.REQUESTS_HEADERS 175 | ); 176 | 177 | # return pd.DataFrame(r.json()['orders']); 178 | return pd.json_normalize(r.json()['orders']['order']); 179 | 180 | 181 | def get_positions(self, symbols=False, equities=False, options=False): 182 | ''' 183 | Fetch and filter position data from the Tradier Account API. 184 | 185 | This function makes a GET request to the Tradier Account API to retrieve position 186 | information related to a trading account. The API response is expected to be in 187 | JSON format, containing details about the positions held in the account. 188 | 189 | Args: 190 | symbols (list, optional): A list of trading symbols (e.g., stock ticker symbols) 191 | to filter the position data. If provided, only positions 192 | matching these symbols will be included. 193 | equities (bool, optional): If True, filter the positions to include only equities 194 | (stocks) with symbols less than 5 characters in length. 195 | If False, no filtering based on equities will be applied. 196 | options (bool, optional): If True, filter the positions to include only options 197 | with symbols exceeding 5 characters in length. 198 | If False, no filtering based on options will be applied. 199 | 200 | Returns: 201 | pandas.DataFrame: A DataFrame containing filtered position information based on 202 | the specified criteria. 203 | 204 | Example: 205 | # Retrieve all positions without filtering 206 | all_positions = get_positions() 207 | 208 | # Retrieve positions for specific symbols ('AAPL', 'GOOGL') 209 | specific_positions = get_positions(symbols=['AAPL', 'GOOGL']) 210 | 211 | # Retrieve only equities 212 | equities_positions = get_positions(equities=True) 213 | 214 | # Retrieve only options 215 | options_positions = get_positions(options=True) 216 | ''' 217 | r = requests.get(url='{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_POSITIONS_ENDPOINT), params={}, headers=self.REQUESTS_HEADERS); 218 | if r.json(): 219 | positions_df = pd.DataFrame(r.json()['positions']['position']); 220 | if symbols: 221 | positions_df = positions_df.query('symbol in @symbols'); 222 | if equities: 223 | positions_df = positions_df[positions_df['symbol'].str.len() < 5]; 224 | options = False; 225 | if options: 226 | positions_df = positions_df[positions_df['symbol'].str.len() > 5]; 227 | return positions_df; -------------------------------------------------------------------------------- /Legacy/approx_entropy.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | import numpy as np 3 | 4 | 5 | def ApEn(U, m, r) -> float: 6 | 7 | """ 8 | Compute the Approximate Entropy (ApEn) of a given time series. 9 | 10 | Approximate Entropy is a measure of the complexity or irregularity of a time series. 11 | It quantifies the likelihood that similar patterns of data points will remain similar 12 | when the time series is extended by one data point. 13 | 14 | Args: 15 | U (list): The input time series as a list of numerical data points. 16 | m (int): The embedding dimension, representing the length of compared subsequences. 17 | r (float): The tolerance parameter that defines the maximum difference allowed between 18 | data points of two subsequences to consider them as similar. 19 | 20 | Returns: 21 | float: The computed Approximate Entropy value. 22 | 23 | Note: 24 | This function uses numpy and assumes that the module 'config' is imported. 25 | 26 | References: 27 | 1. Pincus, S. M. (1991). Approximate entropy as a measure of system complexity. 28 | Proceedings of the National Academy of Sciences, 88(6), 2297-2301. 29 | 2. Richman, J. S., & Moorman, J. R. (2000). Physiological time-series analysis 30 | using approximate entropy and sample entropy. American Journal of Physiology-Heart 31 | and Circulatory Physiology, 278(6), H2039-H2049. 32 | """ 33 | 34 | def _maxdist(x_i, x_j): 35 | return max([abs(ua - va) for ua, va in zip(x_i, x_j)]) 36 | 37 | def _phi(m): 38 | x = [[U[j] for j in range(i, i + m - 1 + 1)] for i in range(N - m + 1)] 39 | C = [ 40 | len([1 for x_j in x if _maxdist(x_i, x_j) <= r]) / (N - m + 1.0) 41 | for x_i in x 42 | ] 43 | return (N - m + 1.0) ** (-1) * sum(np.log(C)) 44 | 45 | N = len(U) 46 | 47 | return abs(_phi(m + 1) - _phi(m)) -------------------------------------------------------------------------------- /Legacy/buy_close_sell_open.py: -------------------------------------------------------------------------------- 1 | from main import * 2 | 3 | # 4 | # Simple script to buy Microsoft at the end of the trading day and sell it at the start of the trading day. 5 | # 6 | 7 | def buy_MSFT (): 8 | print ('Buying MSFT'); 9 | equity_market_order (symbol='MSFT', side='buy', quantity=10.0, duration='day'); 10 | print('Done.\n'); 11 | 12 | def sell_MSFT (): 13 | print ('Selling MSFT'); 14 | equity_market_order (symbol='MSFT', side='sell', quantity=10.0, duration='day'); 15 | print ('Done.\n'); 16 | 17 | 18 | schedule.every().day.at("15:59:00").do(buy_MSFT); 19 | schedule.every().day.at("09:33:00").do(sell_MSFT); 20 | 21 | print('Running Buy/Sell MSFT script...\n'); 22 | while True: 23 | schedule.run_pending(); 24 | time.sleep(1); 25 | -------------------------------------------------------------------------------- /Legacy/cancel_pending_order.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # 4 | # HTTP delete call to order endpoint 5 | # 6 | 7 | def cancel_pending_order (order_id): 8 | ''' 9 | This function will set the order status to 'canceled' for an order whose status was 'pending'. 10 | To get the order_id argument, use the get_orders function. 11 | ''' 12 | 13 | ORDER_CANCEL_ENDPOINT = "v1/accounts/{}/orders/{}".format(ACCOUNT_NUMBER, order_id); 14 | 15 | r = requests.delete( 16 | url = "{}/{}".format(SANDBOX_URL, ORDER_CANCEL_ENDPOINT), 17 | data = {}, 18 | headers = REQUESTS_HEADERS 19 | ); 20 | 21 | return r.json(); -------------------------------------------------------------------------------- /Legacy/combo_order.py: -------------------------------------------------------------------------------- 1 | # Version 3.6.1 2 | import requests 3 | 4 | response = requests.post( 5 | # url = 'https://api.tradier.com/v1/accounts/{account_id}/orders', 6 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 7 | data={ 8 | 'class' : 'combo', 9 | 'symbol' : 'SPY', 10 | 'type' : 'market', 11 | 'duration' : 'day', 12 | 'price' : '1.00', 13 | 14 | 'side[0]' : 'buy', 15 | 'quantity[0]' : '1', 16 | 17 | 'option_symbol[1]' : 'SPY140118C00195000', 18 | 'side[1]' : 'buy_to_open', 19 | 'quantity[1]' : '100', 20 | 21 | 'option_symbol[2]' : 'SPY140118C00196000', 22 | 'side[2]' : 'buy_to_close', 23 | 'quantity[2]' : '100', 24 | 'tag' : 'my-tag-example-1' 25 | }, 26 | headers={'Authorization': 'Bearer ', 'Accept': 'application/json'} 27 | ) 28 | json_response = response.json() 29 | print(response.status_code) 30 | print(json_response) -------------------------------------------------------------------------------- /Legacy/condor_ex.py: -------------------------------------------------------------------------------- 1 | from main import * 2 | 3 | put1 = list(option_chain_day ('FTV').query('strike == 70.0 & option_type == "put"')['symbol'])[0]; 4 | put2 = list(option_chain_day ('FTV').query('strike == 75.0 & option_type == "put"')['symbol'])[0]; 5 | 6 | call1 = list(option_chain_day('FTV').query('strike == 80.0 & option_type == "call"')['symbol'])[0]; 7 | call2 = list(option_chain_day('FTV').query('strike == 85.0 & option_type == "call"')['symbol'])[0]; 8 | 9 | 10 | 11 | # 12 | # Submit order for put credit spread 13 | # 14 | 15 | # side[index] Form String Required buy_to_open 16 | # The side of the leg. One of: buy_to_open, buy_to_close, sell_to_open, sell_to_close 17 | 18 | response = requests.post( 19 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 20 | data = { 21 | 'class': 'multileg', 22 | 'symbol': 'FTV', 23 | 'type': 'market', 24 | 'duration': 'gtc', 25 | 26 | 'option_symbol[0]': put1, 27 | 'side[0]': 'buy_to_open', 28 | 'quantity[0]': '2', 29 | 30 | 'option_symbol[1]': put2, 31 | 'side[1]': 'sell_to_open', 32 | 'quantity[1]': '2' 33 | }, 34 | headers = REQUESTS_HEADERS 35 | ); 36 | 37 | 38 | # 39 | # Submit order for call credit spread 40 | # 41 | 42 | response = requests.post( 43 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 44 | data = { 45 | 'class': 'multileg', 46 | 'symbol': 'FTV', 47 | 'type': 'market', 48 | 'duration': 'gtc', 49 | 50 | 'option_symbol[0]': call1, 51 | 'side[0]': 'sell_to_open', 52 | 'quantity[0]': '2', 53 | 54 | 'option_symbol[1]': call2, 55 | 'side[1]': 'buy_to_open', 56 | 'quantity[1]': '2' 57 | }, 58 | headers = REQUESTS_HEADERS 59 | ) 60 | 61 | 62 | 63 | 64 | 65 | print('put1 = {}'.format(put1)) 66 | print('put2 = {}'.format(put2)) 67 | print('call1 = {}'.format(call1)) 68 | print('call2 = {}'.format(call2)) -------------------------------------------------------------------------------- /Legacy/config.py: -------------------------------------------------------------------------------- 1 | import os; 2 | import dotenv; 3 | import requests; 4 | import numpy as np; 5 | import pandas as pd; 6 | 7 | import datetime; 8 | from datetime import datetime, timedelta; # for fetching option expiries 9 | import re; # parsing option symbols into constituent components 10 | 11 | import schedule; 12 | import time; 13 | 14 | dotenv.load_dotenv(); 15 | 16 | 17 | # 18 | # Fetch account credentials 19 | # 20 | 21 | ACCOUNT_NUMBER = os.getenv('tradier_acct'); 22 | AUTH_TOKEN = os.getenv('tradier_token'); 23 | 24 | 25 | # 26 | # Define headers for convenience because of repeated use with requests 27 | # 28 | 29 | REQUESTS_HEADERS = { 30 | 'Authorization' : 'Bearer {}'.format(AUTH_TOKEN), 31 | 'Accept' : 'application/json' 32 | } 33 | 34 | 35 | # 36 | # Define base url for paper trading and individual API endpoints 37 | # 38 | 39 | SANDBOX_URL = 'https://sandbox.tradier.com'; 40 | 41 | # 42 | # Account endpoints 43 | # 44 | 45 | PROFILE_ENDPOINT = "v1/user/profile"; # GET 46 | 47 | POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(ACCOUNT_NUMBER); # GET 48 | 49 | 50 | ACCOUNT_BALANCE_ENDPOINT = "v1/accounts/{}/balances".format(ACCOUNT_NUMBER); # GET 51 | ACCOUNT_GAINLOSS_ENDPOINT = "v1/accounts/{}/gainloss".format(ACCOUNT_NUMBER); 52 | ACCOUNT_HISTORY_ENDPOINT = "v1/accounts/{}/history".format(ACCOUNT_NUMBER); # GET 53 | ACCOUNT_POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(ACCOUNT_NUMBER); # GET 54 | 55 | 56 | # 57 | # Order endpoint 58 | # 59 | 60 | ORDER_ENDPOINT = "v1/accounts/{}/orders".format(ACCOUNT_NUMBER); # POST 61 | 62 | 63 | 64 | # 65 | # Equity data endpoints 66 | # 67 | 68 | QUOTES_ENDPOINT = "v1/markets/quotes"; # GET (POST) 69 | QUOTES_HISTORICAL_ENDPOINT = "v1/markets/history"; # GET 70 | QUOTES_TIMESALES_ENDPOINT = "v1/markets/timesales"; # GET 71 | QUOTES_SEARCH_ENDPOINT = "v1/markets/search"; # GET 72 | 73 | 74 | 75 | # 76 | # Option data endpoints 77 | # 78 | 79 | OPTION_STRIKE_ENDPOINT = "v1/markets/options/strikes"; # GET 80 | OPTION_CHAIN_ENDPOINT = "v1/markets/options/chains"; # GET 81 | OPTION_EXPIRY_ENDPOINT = "v1/markets/options/expirations"; # GET 82 | OPTION_SYMBOL_ENDPOINT = "v1/markets/options/lookup"; # GET 83 | 84 | 85 | 86 | # 87 | # Intraday market status endpoint 88 | # 89 | 90 | CLOCK_ENDPOINT = "v1/markets/clock"; # GET 91 | 92 | 93 | 94 | # 95 | # Market calendar endpoint 96 | # 97 | 98 | CALENDAR_ENDPOINT = 'v1/markets/calendar'; # GET 99 | -------------------------------------------------------------------------------- /Legacy/data.py: -------------------------------------------------------------------------------- 1 | import datetime; 2 | import os, os.path; 3 | from abc import ABCMeta, abstractmethod; 4 | from event import MarketEvent; 5 | 6 | 7 | 8 | class DataHandler (object): 9 | ''' 10 | Abstract Base Class 11 | 12 | Derived DataHandler classes should output OHLCV vars for each requested symbol. 13 | Replicates live strategy that encounters market data sequentially 14 | ''' 15 | 16 | __metaclass__ = ABCMeta; 17 | 18 | @abstractmethod 19 | def get_latest_bars (self, symbol, N=1): 20 | raise NotImplementedError('Implement get_latest_bars()'); 21 | 22 | @abstractmethod 23 | def update_bars (self): 24 | raise NotImplementedError('Implement update_bars()'); 25 | 26 | 27 | class HistoricCSVDataHandler (DataHandler): 28 | def __init__ (self, events, csv_dir, symbol_list): 29 | self.events = events; 30 | self.csv_dir = csv_dir; 31 | self.symbol_list = symbol_list; 32 | 33 | self.symbol_data = {}; 34 | self.latest_symbol_data = {}; 35 | self.continue_backtest = True; 36 | 37 | self._open_convert_csv_files(); -------------------------------------------------------------------------------- /Legacy/demo.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | from tradier import Tradier; 4 | 5 | from account import Account; -------------------------------------------------------------------------------- /Legacy/equity_orders.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | 4 | # 5 | # Post data for equity market order 6 | # 7 | 8 | def equity_market_order (symbol, side, quantity, duration='day'): 9 | ''' 10 | This function will place a simple market order for the supplied symbol. 11 | By default, the order is good for the day. Per the nature of market orders, it should be filled. 12 | 13 | Parameter Notes: 14 | side = buy, buy_to_cover, sell, sell_short 15 | duration = day, gtc, pre, post 16 | ''' 17 | 18 | r = requests.post( 19 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 20 | params = { 21 | 'class' : 'equity', 22 | 'symbol' : symbol, 23 | 'side' : side, 24 | 'quantity' : quantity, 25 | 'type' : 'market', 26 | 'duration' : duration 27 | }, 28 | headers = REQUESTS_HEADERS 29 | ); 30 | 31 | return r.json(); 32 | 33 | 34 | # 35 | # Post data for equity limit order 36 | # 37 | 38 | def equity_limit_order (symbol, side, quantity, limit_price, duration='day'): 39 | ''' 40 | This function places a limit order to buy/sell the given symbol at the specified limit_price (or better). 41 | Recall that a limit order guarantees the execution price. However, the order might not execute at all. 42 | 43 | Parameter Notes: 44 | side = buy, buy_to_cover, sell, sell_short 45 | duration = day, gtc, pre, post 46 | ''' 47 | 48 | r = requests.post( 49 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 50 | data = { 51 | 'class' : 'equity', 52 | 'symbol' : symbol, 53 | 'side' : side, 54 | 'quantity' : quantity, 55 | 'type' : 'limit', 56 | 'duration' : duration, 57 | 'price' : limit_price 58 | }, 59 | headers = REQUESTS_HEADERS 60 | ); 61 | 62 | return r.json(); 63 | 64 | 65 | 66 | 67 | # 68 | # Post data for equity stop-loss or stop-entry orders 69 | # 70 | 71 | def equity_stop_order (symbol, side, quantity, stop_price, duration='day'): 72 | ''' 73 | This function places a stop-loss or stop-entry order to buy/sell equities. 74 | Recall that a stop order will trigger in the direction of the stock's movement 75 | 76 | Parameter Notes: 77 | side = buy, buy_to_cover, sell, sell_short 78 | duration = day, gtc, pre, post 79 | ''' 80 | 81 | r = requests.post( 82 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 83 | data = { 84 | 'class' : 'equity', 85 | 'symbol' : symbol, 86 | 'side' : side, 87 | 'quantity' : quantity, 88 | 'type' : 'stop', 89 | 'duration' : duration, 90 | 'stop' : stop_price 91 | }, 92 | headers = REQUESTS_HEADERS 93 | ); 94 | 95 | return r.json(); 96 | 97 | 98 | 99 | 100 | 101 | # 102 | # Post data for equity stop-limit order 103 | # 104 | 105 | def equity_stop_limit_order (symbol, side, quantity, stop_price, limit_price, duration='day'): 106 | ''' 107 | This function places a stop limit order with user-specified stop and limit prices. 108 | Recall that the stop_price indicates the price at which the order will convert into a limit order. 109 | (This contrasts an ordinary stop order, which will convert into a market order at the stop price.) 110 | 111 | The limit_price indicates the limit price once the order becomes a limit order. 112 | 113 | Buy stop limit orders are placed with a price in excess of the current stock price. 114 | Sell stop limit orders are placed below the current stock price. 115 | 116 | Parameter Notes: 117 | stop_price = stop price 118 | limit_price = limit price 119 | side = buy, buy_to_cover, sell, sell_short 120 | duration = day, gtc, pre, post 121 | 122 | ''' 123 | r = requests.post( 124 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 125 | data = { 126 | 'class' : 'equity', 127 | 'symbol' : symbol, 128 | 'side' : side, 129 | 'quantity' : quantity, 130 | 'type' : 'stop_limit', 131 | 'duration' : duration, 132 | 'price' : limit_price, 133 | 'stop' : stop_price 134 | }, 135 | headers = REQUESTS_HEADERS 136 | ); 137 | 138 | return r.json(); -------------------------------------------------------------------------------- /Legacy/event.py: -------------------------------------------------------------------------------- 1 | # event.py 2 | 3 | class Event(object): 4 | """ 5 | Event is base class providing an interface for all subsequent 6 | (inherited) events, that will trigger further events in the 7 | trading infrastructure. 8 | """ 9 | pass 10 | 11 | 12 | # event.py 13 | 14 | class MarketEvent(Event): 15 | """ 16 | Handles the event of receiving a new market update with 17 | corresponding bars. 18 | """ 19 | 20 | def __init__(self): 21 | """ 22 | Initialises the MarketEvent. 23 | """ 24 | self.type = 'MARKET' 25 | 26 | 27 | 28 | 29 | class SignalEvent (Event): 30 | def __init__ (self, symbol, datetime, signal_type): 31 | ''' 32 | Initialize SignalEvent object. 33 | 34 | Params: 35 | symbol = stock (e.g. 'AAPL') 36 | datetime = timestamp of when event occurred 37 | signal_type = 'LONG' or 'SHORT' 38 | ''' 39 | 40 | self.type = 'SIGNAL'; 41 | self.symbol = symbol; 42 | self.datetime = datetime; 43 | self.signal_type = signal_type; 44 | 45 | 46 | 47 | 48 | 49 | class OrderEvent (Event): 50 | ''' 51 | Sends orders to execution system. 52 | ''' 53 | 54 | def __init__ (self, symbol, order_type, quantity, direction): 55 | ''' 56 | Params: 57 | symbol = asset 58 | order_type = 'MRK', 'LMT' 59 | quantity = non-negative integer 60 | direction = 'BUY', 'SELL' 61 | ''' 62 | 63 | self.type = 'ORDER'; 64 | self.symbol = symbol; 65 | self.order_type = order_type; 66 | self.quantity = quantity; 67 | self.direction = direction; 68 | 69 | def print_order (self): 70 | print("Order: Symbol={symbol}, Type={type}, Quantity={quantity}, Direction={direction}".format(symbol=self.symbol, type=self.type, quantity=self.quantity, direction=self.direction)); 71 | 72 | 73 | 74 | 75 | class FillEvent (Event): 76 | def __init__ (self, timeindex, symbol, exchange, quantity, direction, fill_cost, commission=None): 77 | self.type = 'FILL'; 78 | self.timeindex = timeindex; 79 | self.symbol = symbol; 80 | self.exchange = exchange; 81 | self.quantity = quantity; 82 | self.direction = direction; 83 | self.fill_cost = fill_cost; 84 | self.commission = commission; 85 | 86 | 87 | 88 | 89 | 90 | # event.py 91 | 92 | # class FillEvent(Event): 93 | # """ 94 | # Encapsulates the notion of a Filled Order, as returned 95 | # from a brokerage. Stores the quantity of an instrument 96 | # actually filled and at what price. In addition, stores 97 | # the commission of the trade from the brokerage. 98 | # """ 99 | 100 | # def __init__(self, timeindex, symbol, exchange, quantity, 101 | # direction, fill_cost, commission=None): 102 | # """ 103 | # Initialises the FillEvent object. Sets the symbol, exchange, 104 | # quantity, direction, cost of fill and an optional 105 | # commission. 106 | 107 | # If commission is not provided, the Fill object will 108 | # calculate it based on the trade size and Interactive 109 | # Brokers fees. 110 | 111 | # Parameters: 112 | # timeindex - The bar-resolution when the order was filled. 113 | # symbol - The instrument which was filled. 114 | # exchange - The exchange where the order was filled. 115 | # quantity - The filled quantity. 116 | # direction - The direction of fill ('BUY' or 'SELL') 117 | # fill_cost - The holdings value in dollars. 118 | # commission - An optional commission sent from IB. 119 | # """ 120 | 121 | # # self.type = 'FILL' 122 | # # self.timeindex = timeindex 123 | # # self.symbol = symbol 124 | # # self.exchange = exchange 125 | # # self.quantity = quantity 126 | # # self.direction = direction 127 | # # self.fill_cost = fill_cost 128 | 129 | # # # Calculate commission 130 | # # if commission is None: 131 | # # self.commission = self.calculate_ib_commission() 132 | # # else: 133 | # # self.commission = commission 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /Legacy/get_account_balance.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | def get_account_balance (): 4 | r = requests.get( 5 | url = '{}/{}'.format(SANDBOX_URL, ACCOUNT_BALANCE_ENDPOINT), 6 | params = {}, 7 | headers = REQUESTS_HEADERS 8 | ); 9 | 10 | return pd.json_normalize(r.json()['balances']); -------------------------------------------------------------------------------- /Legacy/get_cancelled_orders.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | from get_orders import get_orders; 4 | 5 | def rejected_order_by_symbol (symbol_rejected): 6 | df_orders = get_orders(); 7 | return int(df_orders.query('symbol==@symbol_rejected & status=="rejected"')['id']); -------------------------------------------------------------------------------- /Legacy/get_clock.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | 4 | def get_clock (): 5 | r = requests.get( 6 | url = '{}/{}'.format(SANDBOX_URL, CLOCK_ENDPOINT), 7 | params = {'delayed':'false'}, 8 | headers = REQUESTS_HEADERS 9 | ); 10 | 11 | return pd.json_normalize(r.json()['clock']); 12 | 13 | -------------------------------------------------------------------------------- /Legacy/get_gainloss.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | def get_gainloss (symbols='', results_limit='', sort_dates='closeDate', sort_symbols='', start_date='', end_date=''): 4 | params_dict = {'sortBy':sort_dates}; 5 | 6 | if results_limit: 7 | params_dict['limit'] = results_limit; 8 | 9 | if sort_symbols: 10 | params_dict['sort'] = sort_symbols; 11 | 12 | if start_date: 13 | params_dict['start'] = start_date; 14 | 15 | if end_date: 16 | params_dict['end'] = end_date; 17 | 18 | r = requests.get( 19 | url = '{}/{}'.format(SANDBOX_URL, ACCOUNT_GAINLOSS_ENDPOINT), 20 | params = params_dict, 21 | headers = REQUESTS_HEADERS 22 | ); 23 | 24 | gainloss_df = pd.json_normalize(r.json()['gainloss']['closed_position']); 25 | 26 | return gainloss_df; -------------------------------------------------------------------------------- /Legacy/get_historical_quotes.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # 4 | # Helper function used to index the start of the trading week 5 | # 6 | 7 | def last_monday (input_date): 8 | ''' 9 | Find the date of the previous Monday for a given input date. 10 | 11 | Args: 12 | input_date (datetime.date): the input date 13 | 14 | Returns: 15 | datetime.date: The date of the previous Monday. 16 | ''' 17 | 18 | return (input_date - timedelta(days=(input_date.weekday()))); 19 | 20 | 21 | # 22 | # Fetch historical stock data 23 | # 24 | 25 | def get_historical_quotes (symbol, interval='daily', start_date=False, end_date=False): 26 | ''' 27 | Fetch historical stock data for a given symbol within a specified date range. 28 | 29 | Args: 30 | symbol (str): The stock ticker symbol. 31 | 32 | interval (str, optional) The interval of data. Default is 'daily'. Alt values are weekly and monthly. 33 | 34 | start_date (str, optional) The start of the date range in 'YYYY-MM-DD' format. 35 | If not provided, the function sets the start_date to be the Monday preceding the end_date. 36 | 37 | end_date (str, optional) The end of the date range in 'YYYY-MM-DD' format. 38 | If not provided, the end_date is set to be the current date. 39 | 40 | Returns: 41 | pandas.DataFrame: A DataFrame with columns: [date, open, high, low, close, volume] 42 | 43 | Note: 44 | This function requires that global constants SANDBOX_URL, QUOTES_HISTORICAL_ENDPOINT, and REQUESTS_HEADERS be defined in config. 45 | 46 | Example: 47 | get_historical_quotes (symbol='MMM', interval='daily', start_date='2023-06-01', end_date='2023-06-12') 48 | ''' 49 | 50 | if not end_date: 51 | end_date = datetime.today().strftime('%Y-%m-%d'); 52 | 53 | if not start_date: 54 | tmp = datetime.strptime(end_date, '%Y-%m-%d'); 55 | start_date = last_monday(tmp).strftime('%Y-%m-%d'); 56 | 57 | r = requests.get( 58 | url = '{}/{}'.format(SANDBOX_URL, QUOTES_HISTORICAL_ENDPOINT), 59 | params = { 60 | 'symbol' : symbol, 61 | 'interval' : interval, 62 | 'start' : start_date, 63 | 'end' : end_date 64 | }, 65 | headers = REQUESTS_HEADERS 66 | ); 67 | 68 | return pd.DataFrame(r.json()['history']['day']); 69 | 70 | -------------------------------------------------------------------------------- /Legacy/get_market_calendar.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | 4 | def get_market_calendar (month=False, year=False, days_df=False): 5 | params = {}; 6 | 7 | if month: 8 | params['month'] = month; 9 | if year: 10 | params['year'] = year; 11 | 12 | r = requests.get( 13 | url = '{}/{}'.format(SANDBOX_URL, CALENDAR_ENDPOINT), 14 | params = params, 15 | headers = REQUESTS_HEADERS 16 | ); 17 | 18 | if days_df: 19 | return pd.DataFrame(r.json()['calendar']['days']['day']); 20 | 21 | return r.json()['calendar']; -------------------------------------------------------------------------------- /Legacy/get_option_symbols.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | 4 | # 5 | # This function returns a list of symbols. Each element has a format 6 | # COP240216C00115000 7 | # 8 | # i.e. 9 | # 10 | # COP 240216 C 00115000 11 | # 12 | 13 | def get_option_symbols (underlying_symbol, df=False): 14 | ''' 15 | This function provides a convenient wrapper to fetch option symbols 16 | for a specified underlying_symbol 17 | ''' 18 | r = requests.get( 19 | url = '{}/{}'.format(SANDBOX_URL, OPTION_SYMBOL_ENDPOINT), 20 | params = {'underlying':underlying_symbol}, 21 | headers = REQUESTS_HEADERS 22 | ); 23 | 24 | option_list = r.json()['symbols'][0]['options']; 25 | 26 | if df: 27 | option_df = symbol_list_to_df(option_list); 28 | option_df['expiration_date'] = parse_option_expiries(option_df['expiration_date']); 29 | return option_df; 30 | 31 | return option_list; 32 | 33 | 34 | 35 | 36 | # 37 | # Helper function to convert the get_option_symbols list into a dataframe 38 | # 39 | 40 | def symbol_list_to_df (option_list): 41 | ''' 42 | This is a helper function called from get_option_symbols. 43 | It will parse a list of option symbols into their constituent components 44 | For example: 45 | option_symbol 46 | = [underlying_symbol, expiry_date, option_type, option_id] 47 | = [LMT, 240119, C, 00300000] 48 | ''' 49 | parsed_options = [] 50 | 51 | for option in option_list: 52 | match = re.match(r'([A-Z]+)(\d{6})([CP])(\d+)', option) 53 | if match: 54 | root_symbol, expiration_date, option_type, strike_price = match.groups() 55 | parsed_options.append({ 56 | 'symbol' : option, 57 | 'root_symbol' : root_symbol, 58 | 'expiration_date' : expiration_date, 59 | 'option_type' : option_type, 60 | 'strike_price' : strike_price 61 | }) 62 | return pd.DataFrame(parsed_options); 63 | 64 | 65 | 66 | 67 | # 68 | # Helper function to turn the option dates into standard date format 69 | # 70 | 71 | def parse_option_expiries (expiry_list): 72 | ''' 73 | Helper function to turn the option dates into standard date format 74 | ''' 75 | 76 | formatted_expiries = []; 77 | for x in expiry_list: 78 | formatted_expiries.append(datetime.strptime(x, '%y%m%d').strftime('%Y-%m-%d')); 79 | 80 | return formatted_expiries; -------------------------------------------------------------------------------- /Legacy/get_orders.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # 4 | # Fetch current order queue 5 | # 6 | 7 | def get_orders (): 8 | r = requests.get( 9 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 10 | params = {'includeTags':'true'}, 11 | headers = REQUESTS_HEADERS 12 | ); 13 | 14 | return pd.DataFrame(r.json()['orders']['order']); -------------------------------------------------------------------------------- /Legacy/get_positions.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | def get_positions(symbols=False, equities=False, options=False): 4 | ''' 5 | Fetch and filter position data from the Tradier Account API. 6 | 7 | This function makes a GET request to the Tradier Account API to retrieve position 8 | information related to a trading account. The API response is expected to be in 9 | JSON format, containing details about the positions held in the account. 10 | 11 | Args: 12 | symbols (list, optional): A list of trading symbols (e.g., stock ticker symbols) 13 | to filter the position data. If provided, only positions 14 | matching these symbols will be included. 15 | equities (bool, optional): If True, filter the positions to include only equities 16 | (stocks) with symbols less than 5 characters in length. 17 | If False, no filtering based on equities will be applied. 18 | options (bool, optional): If True, filter the positions to include only options 19 | with symbols exceeding 5 characters in length. 20 | If False, no filtering based on options will be applied. 21 | 22 | Returns: 23 | pandas.DataFrame: A DataFrame containing filtered position information based on 24 | the specified criteria. 25 | 26 | Note: 27 | • Before using this function, make sure to define the necessary constants, 28 | such as SANDBOX_URL, POSITIONS_ENDPOINT, and REQUESTS_HEADERS, with the 29 | appropriate values in a configuration module. 30 | 31 | Example: 32 | # Retrieve all positions without filtering 33 | all_positions = get_positions() 34 | 35 | # Retrieve positions for specific symbols ('AAPL', 'GOOGL') 36 | specific_positions = get_positions(symbols=['AAPL', 'GOOGL']) 37 | 38 | # Retrieve only equities 39 | equities_positions = get_positions(equities=True) 40 | 41 | # Retrieve only options 42 | options_positions = get_positions(options=True) 43 | ''' 44 | r = requests.get(url='{}/{}'.format(SANDBOX_URL, ACCOUNT_POSITIONS_ENDPOINT), params={}, headers=REQUESTS_HEADERS); 45 | 46 | positions_df = pd.DataFrame(r.json()['positions']['position']); 47 | 48 | if symbols: 49 | positions_df = positions_df.query('symbol in @symbols'); 50 | 51 | if equities: 52 | positions_df = positions_df[positions_df['symbol'].str.len() < 5]; 53 | options = False; 54 | 55 | if options: 56 | positions_df = positions_df[positions_df['symbol'].str.len() > 5]; 57 | 58 | return positions_df; 59 | -------------------------------------------------------------------------------- /Legacy/get_quote.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | def get_quote_day (symbol, last_price=False): 4 | ''' 5 | This function fetches the current quote data about a given symbol 6 | 7 | Example Usage: 8 | Fetch the last price of a given stock: last_price = get_quote_day('VMC', last_price=True) 9 | ''' 10 | 11 | r = requests.get( 12 | url = '{}/{}'.format(SANDBOX_URL, QUOTES_ENDPOINT), 13 | params = {'symbols':symbol, 'greeks':'false'}, 14 | headers = REQUESTS_HEADERS 15 | ); 16 | 17 | df_quote = pd.json_normalize(r.json()['quotes']['quote']); 18 | 19 | if last_price: 20 | return float(df_quote['last']); 21 | 22 | return df_quote; -------------------------------------------------------------------------------- /Legacy/get_timesales.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # def get_timesales (symbol, interval='1min', start_date=False, end_date=False): 4 | 5 | # r = requests.get( 6 | # url = '{}/{}'.format(SANDBOX_URL, QUOTES_TIMESALES_ENDPOINT), 7 | # params = { 8 | # 'symbol' : symbol, 9 | # 'interval' : interval, 10 | # '' 11 | # }) -------------------------------------------------------------------------------- /Legacy/getoptionchain.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | from getoptionexpiry import get_expiry_dates; 3 | 4 | 5 | 6 | # 7 | # Fetch all option chain data for a single day of contract expirations 8 | # 9 | 10 | def option_chain_day (symbol, expiry='', strike_low=False, strike_high=False, option_type=False): 11 | ''' 12 | This function returns option chain data for a given symbol. 13 | All contract expirations occur on the same expiry date 14 | ''' 15 | 16 | # 17 | # Set the contract expiration date to the nearest valid date 18 | # 19 | 20 | if not expiry: 21 | expiry = get_expiry_dates(symbol)[0]; 22 | 23 | # 24 | # Define request object for given symbol and expiration 25 | # 26 | 27 | r = requests.get( 28 | url = '{}/{}'.format(SANDBOX_URL, OPTION_CHAIN_ENDPOINT), 29 | params = {'symbol':symbol, 'expiration':expiry, 'greeks':'false'}, 30 | headers = REQUESTS_HEADERS 31 | ); 32 | 33 | # 34 | # Convert returned json -> pandas dataframe 35 | # 36 | 37 | option_df = pd.DataFrame(r.json()['options']['option']); 38 | 39 | 40 | # 41 | # Remove columns which have the same value for every row 42 | # 43 | 44 | cols_to_drop = option_df.nunique()[option_df.nunique() == 1].index; 45 | option_df = option_df.drop(cols_to_drop, axis=1); 46 | 47 | # 48 | # Remove columns which have NaN in every row 49 | # 50 | 51 | cols_to_drop = option_df.nunique()[option_df.nunique() == 0].index; 52 | option_df = option_df.drop(cols_to_drop, axis=1); 53 | 54 | 55 | # 56 | # Remove description column because its information is redundant 57 | # 58 | 59 | cols_to_drop = ['description']; 60 | option_df = option_df.drop(cols_to_drop, axis=1); 61 | 62 | 63 | # 64 | # Filter rows per strike_low and strike_high 65 | # 66 | 67 | if strike_low: 68 | option_df = option_df.query('strike >= @strike_low'); 69 | 70 | if strike_high: 71 | option_df = option_df.query('strike <= @strike_high'); 72 | 73 | if option_type in ['call', 'put']: 74 | option_df = option_df.query('option_type == @option_type'); 75 | 76 | 77 | if option_type: 78 | if option_type in ['call', 'put']: 79 | option_df = option_df.query('option_type == @option_type'); 80 | 81 | # 82 | # Return the resulting dataframe whose rows are individual contracts with expiration `expiry` 83 | # 84 | 85 | return option_df; 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Legacy/getoptionexpiry.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | 4 | 5 | # 6 | # Wrapper function to get contract expiration dates 7 | # 8 | 9 | def get_expiry_dates (symbol, strikes=False): 10 | ''' 11 | This returns a list of valid dates on which option contracts expire. 12 | ''' 13 | r = requests.get( 14 | url = '{}/{}'.format(SANDBOX_URL, OPTION_EXPIRY_ENDPOINT), 15 | params = {'symbol':symbol, 'includeAllRoots':True, 'strikes':str(strikes)}, 16 | headers = REQUESTS_HEADERS 17 | ); 18 | 19 | if r.status_code != 200: 20 | return 'wtf'; 21 | 22 | expiry_dict = r.json()['expirations']; 23 | 24 | # Without strikes, we can get a list of dates 25 | if strikes == False: 26 | return expiry_dict['date']; 27 | 28 | # Otherwise, return a list whose elements are dicts with format {'date':[list_of_strikes]} 29 | return expiry_dict['expiration']; -------------------------------------------------------------------------------- /Legacy/getoptionstrike.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | from datetime import datetime, timedelta; 3 | 4 | next_week = (datetime.now() + timedelta(days=3)).strftime('%Y-%m-%d'); 5 | 6 | url = "{}/{}".format(SANDBOX_URL, OPTION_STRIKE_ENDPOINT); 7 | 8 | 9 | # 10 | # Fetch strike prices on DuPont options whose expiration occurs in three days 11 | # 12 | 13 | r = requests.get( 14 | url = url, 15 | params = {'symbol':'DD', 'expiration':next_week}, 16 | headers = {'Authorization':'Bearer {}'.format(AUTH_TOKEN), 'Accept':'application/json'} 17 | ); -------------------------------------------------------------------------------- /Legacy/getuser.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # 4 | # Fetch basic account information 5 | # 6 | 7 | def get_profile (): 8 | ''' 9 | This function returns basic information about user account. 10 | ''' 11 | 12 | r = requests.get( 13 | url = '{}/{}'.format(SANDBOX_URL, PROFILE_ENDPOINT), 14 | params = {}, 15 | headers = REQUESTS_HEADERS 16 | ); 17 | 18 | return pd.json_normalize(r.json()['profile']); -------------------------------------------------------------------------------- /Legacy/idk.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | from main import * 3 | import time 4 | 5 | 6 | 7 | # equity_market_order(symbol, side, quantity, duration='day') 8 | # This function will place a simple market order for the supplied symbol. 9 | # By default, the order is good for the day. Per the nature of market orders, it should be filled. 10 | 11 | # Parameter Notes: 12 | # side = buy, buy_to_cover, sell, sell_short 13 | # duration = day, gtc, pre, post 14 | 15 | # def print_hello_world_every_5_minutes(): 16 | def test_buy_sell (): 17 | while True: 18 | # print("Hello, World") 19 | print('Buying KLAC'); equity_market_order(symbol='KLAC', side='buy', quantity=10.0, duration='day'); time.sleep(15); 20 | print('Selling KLAC'); equity_market_order(symbol='KLAC', side='sell', quantity=10.0, duration='day'); time.sleep(15); 21 | 22 | test_buy_sell(); 23 | -------------------------------------------------------------------------------- /Legacy/logit_data.py: -------------------------------------------------------------------------------- 1 | from tradier import * 2 | 3 | # 4 | # Sample script that will fetch S&P 500 stocks and query the Tradier API to get OLHCV data 5 | # 6 | 7 | sp500 = pd.read_csv('https://raw.githubusercontent.com/datasets/s-and-p-500-companies/main/data/constituents.csv'); 8 | 9 | sp500_sample = sp500.sample(10); 10 | 11 | sp500_symbols = list(sp500_sample['Symbol']); 12 | 13 | df = pd.DataFrame({'symbol':[], 'profitable':[], 'volume_change':[]}); 14 | 15 | for s in sp500_symbols: 16 | bar_data = quotes.get_historical_quotes(s, interval='weekly', start_date='2023-08-01', end_date='2023-10-01'); 17 | bar_data['profitable'] = (bar_data['close'] - bar_data['open'] > 0).astype(int); 18 | bar_data['volume_change'] = bar_data['volume'].diff(); 19 | bar_data['symbol'] = s; 20 | df = df.append(bar_data[['symbol', 'profitable', 'volume_change']]); 21 | 22 | 23 | 24 | # 25 | # TO DO: BUILD LOGISTIC REGRESSION MODEL WITH df 26 | # -------------------------------------------------------------------------------- /Legacy/main.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | from getoptionchain import option_chain_day; 4 | from getoptionexpiry import get_expiry_dates; 5 | from get_quote import get_quote_day; 6 | from getuser import get_profile; 7 | from equity_orders import equity_market_order, equity_limit_order, equity_stop_order, equity_stop_limit_order; 8 | from get_positions import get_positions; 9 | from get_gainloss import get_gainloss; 10 | from get_option_symbols import get_option_symbols, symbol_list_to_df, parse_option_expiries; 11 | from option_orders import option_market_order, bull_call_spread, bull_put_spread, bear_call_spread; 12 | from get_orders import get_orders; 13 | from get_cancelled_orders import rejected_order_by_symbol; 14 | from cancel_pending_order import cancel_pending_order; 15 | from get_clock import get_clock; 16 | from get_market_calendar import get_market_calendar; 17 | from get_account_balance import get_account_balance; 18 | # from buy_close_sell_open import buy_MSFT, sell_MSFT; 19 | from get_historical_quotes import last_monday, get_historical_quotes; 20 | from price_per_share import price_per_share; 21 | 22 | 23 | from straddle_orders import straddle_order; 24 | from married_put import married_put; 25 | 26 | from samp_entropy import construct_templates, get_matches, is_match, sample_entropy; 27 | from approx_entropy import ApEn; 28 | 29 | print('hello, world!'); -------------------------------------------------------------------------------- /Legacy/married_put.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | def married_put (symbol, equity_quantity, option_symbol, option_quantity, duration='day'): 4 | ''' 5 | Married put is a bullish strategy that seeks to limit losses in the event that the underlying asset has an 6 | unexpected decrease in price. It involves the following components: 7 | • long asset 8 | • long ATM put on asset 9 | 10 | Arguments: 11 | symbol: underlying asset 12 | equity_quantity: number of shares of underlying to long 13 | option_symbol: OCC option contract symbol 14 | option_quantity: number of option contracts to purchase 15 | duration: day, gtc, pre, post 16 | 17 | Returns: 18 | json from HTTP post request to indicate success 19 | 20 | ''' 21 | r = requests.post( 22 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 23 | data = { 24 | 'class' : 'combo', 25 | 'symbol' : symbol, 26 | 'type' : 'market', 27 | 'duration' : duration, 28 | 'side[0]' : 'buy', 29 | 'quantity[0]' : equity_quantity, 30 | 'option_symbol[1]' : option_symbol, 31 | 'side[1]' : 'buy_to_open', 32 | 'quantity[1]' : option_quantity 33 | }, 34 | headers = REQUESTS_HEADERS 35 | ); 36 | 37 | return r.json(); -------------------------------------------------------------------------------- /Legacy/option_orders.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | 4 | 5 | 6 | # 7 | # Bear-put spread 8 | # 9 | 10 | def bear_put_spread (underlying, option0, quantity0, option1, quantity1, duration='day'): 11 | r = requests.post( 12 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 13 | data = { 14 | 'class' : 'multileg', 15 | 'symbol' : underlying, 16 | 'type' : 'market', 17 | 'duration' : duration, 18 | 'option_symbol[0]' : option0, 19 | 'side[0]' : 'buy_to_open', 20 | 'quantity[0]' : quantity0, 21 | 'option_symbol[1]' : option1, 22 | 'side[1]' : 'sell_to_open', 23 | 'quantity[1]' : quantity1 24 | }, 25 | headers = REQUESTS_HEADERS 26 | ); 27 | 28 | return r.json(); 29 | 30 | 31 | # 32 | # Bear-call spread 33 | # 34 | 35 | def bear_call_spread (underlying, option0, quantity0, option1, quantity1, duration='day'): 36 | r = requests.post( 37 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 38 | data = { 39 | 'class' : 'multileg', 40 | 'symbol' : underlying, 41 | 'type' : 'market', 42 | 'duration' : duration, 43 | 'option_symbol[0]' : option0, 44 | 'side[0]' : 'buy_to_open', 45 | 'quantity[0]' : quantity0, 46 | 'option_symbol[1]' : option1, 47 | 'side[1]' : 'sell_to_open', 48 | 'quantity[1]' : quantity1 49 | }, 50 | headers = REQUESTS_HEADERS 51 | ); 52 | 53 | return r.json(); 54 | 55 | 56 | 57 | 58 | # 59 | # Bull-put spread 60 | # 61 | 62 | def bull_put_spread (underlying_symbol, option_symbol_0, quantity_0, option_symbol_1, quantity_1, duration='day'): 63 | r = requests.post( 64 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 65 | data = { 66 | 'class' : 'multileg', 67 | 'symbol' : underlying_symbol, 68 | 'type' : 'market', 69 | 'duration' : duration, 70 | 'option_symbol[0]' : option_symbol_0, 71 | 'side[0]' : 'sell_to_open', 72 | 'quantity[0]' : quantity_0, 73 | 'option_symbol[1]' : option_symbol_1, 74 | 'side[1]' : 'buy_to_open', 75 | 'quantity[1]' : quantity_1 76 | }, 77 | headers = REQUESTS_HEADERS 78 | ); 79 | 80 | return r.json(); 81 | 82 | 83 | 84 | 85 | # 86 | # Bull-call spread 87 | # 88 | 89 | def bull_call_spread (underlying_symbol, option_symbol_0, quantity_0, option_symbol_1, quantity_1, duration='day'): 90 | r = requests.post( 91 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 92 | data = { 93 | 'class' : 'multileg', 94 | 'symbol' : underlying_symbol, 95 | 'type' : 'market', 96 | 'duration' : duration, 97 | 'option_symbol[0]' : option_symbol_0, 98 | 'side[0]' : 'buy_to_open', 99 | 'quantity[0]' : quantity_0, 100 | 'option_symbol[1]' : option_symbol_1, 101 | 'side[1]' : 'sell_to_open', 102 | 'quantity[1]' : quantity_1 103 | }, 104 | headers = REQUESTS_HEADERS 105 | ); 106 | 107 | return r.json(); 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | # 116 | # Option contract market order 117 | # 118 | 119 | def option_market_order (underlying_symbol, option_symbol, side, quantity, duration='day'): 120 | ''' 121 | This function places a simple market order to long/short an option contract. 122 | Valid argument values: 123 | side = buy_to_open, buy_to_close, sell_to_open, sell_to_close 124 | duration = day, gtc, pre, post 125 | ''' 126 | r = requests.post( 127 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 128 | data = { 129 | 'class' : 'option', 130 | 'symbol' : underlying_symbol, 131 | 'option_symbol' : option_symbol, 132 | 'side' : side, 133 | 'quantity' : quantity, 134 | 'type' : 'market', 135 | 'duration' : duration 136 | }, 137 | headers = REQUESTS_HEADERS 138 | ); 139 | 140 | print(r.json()); -------------------------------------------------------------------------------- /Legacy/placeorder.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # 4 | # Place market order to buy 10 shares of Microsoft 5 | # 6 | 7 | r = requests.post( 8 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 9 | headers = {'Authorization': 'Bearer {}'.format(AUTH_TOKEN), 'Accept':'application/json'}, 10 | data = {'class':'equity','symbol': 'MSFT','side':'buy','quantity': '10','type': 'market','duration': 'day','tag': 'my-tag-example-1'} 11 | ); 12 | 13 | r_response = r.json(); 14 | print(r_response); -------------------------------------------------------------------------------- /Legacy/plot_timesales_gpt.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | 3 | # Your DataFrame 4 | df = quotes.get_timesales('KO', start_time='2023-09-01') 5 | 6 | # Extract the 'time' and 'close' columns 7 | time_series = df['time'] 8 | close_prices = df['close'] 9 | 10 | # Convert the 'time' column to a datetime object 11 | time_series = pd.to_datetime(time_series) 12 | 13 | # Create the time series plot 14 | plt.figure(figsize=(12, 6)) 15 | plt.plot(time_series, close_prices, label='Close Price', color='blue') 16 | plt.title('Time Series Plot of Close Prices') 17 | plt.xlabel('Time') 18 | plt.ylabel('Close Price') 19 | plt.grid(True) 20 | plt.legend() 21 | plt.xticks(rotation=45) 22 | plt.tight_layout() 23 | 24 | # Show the plot 25 | plt.show() 26 | -------------------------------------------------------------------------------- /Legacy/price_per_share.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | from get_positions import get_positions; 3 | 4 | def price_per_share (symbol): 5 | ''' 6 | Calculate the price-per-share for a equity/option symbol that you currently own. 7 | 8 | This function computes the price-per-share of a symbol found in the `get_positions` returned dataframe. 9 | It filters the `get_positions` data, calculates the price-per-share, and returns the value as a float. 10 | 11 | Arguments: 12 | symbol (str): The trading symbol (e.g., stock symbol or OCC option symbol) whose price-per-share is desired. 13 | 14 | Returns: 15 | float: The computed price-per-share. 16 | ''' 17 | record = get_positions().query('symbol == @symbol'); 18 | return float(record['cost_basis'] / record['quantity']); 19 | -------------------------------------------------------------------------------- /Legacy/samp_entropy.py: -------------------------------------------------------------------------------- 1 | from itertools import combinations 2 | from math import log 3 | from config import * 4 | 5 | def construct_templates(timeseries_data:list, m:int=2): 6 | ''' 7 | Construct templates from the given time series data. 8 | 9 | Args: 10 | timeseries_data (list): The input time series data as a list of numerical data points. 11 | m (int): The embedding dimension, representing the length of templates. Default is 2. 12 | 13 | Returns: 14 | list: A list of templates extracted from the time series data. 15 | 16 | ''' 17 | 18 | num_windows = len(timeseries_data) - m + 1 19 | return [timeseries_data[x:x+m] for x in range(0, num_windows)] 20 | 21 | 22 | def get_matches(templates:list, r:float): 23 | ''' 24 | Count the number of template matches based on the given tolerance parameter. 25 | 26 | Args: 27 | templates (list): A list of templates to compare. 28 | r (float): The tolerance parameter that defines the maximum difference allowed between data points of two templates to consider them as a match. 29 | 30 | Returns: 31 | int: The number of template matches. 32 | 33 | ''' 34 | 35 | return len(list(filter(lambda x: is_match(x[0], x[1], r), combinations(templates, 2)))) 36 | 37 | 38 | def is_match(template_1:list, template_2:list, r:float): 39 | ''' 40 | Check if two templates are a match within the given tolerance. 41 | 42 | Args: 43 | template_1 (list): The first template. 44 | template_2 (list): The second template. 45 | r (float): The tolerance parameter that defines the maximum difference allowed between data points of the two templates to consider them as a match. 46 | 47 | Returns: 48 | bool: True if the templates are a match, False otherwise. 49 | 50 | ''' 51 | 52 | return all([abs(x - y) < r for (x, y) in zip(template_1, template_2)]) 53 | 54 | 55 | def sample_entropy(timeseries_data:list, window_size:int, r:float): 56 | ''' 57 | Calculate the Sample Entropy of the given time series data. 58 | Sample Entropy quantifies the irregularity or complexity of a time series. 59 | 60 | Args: 61 | timeseries_data (list): The input time series data as a list of numerical data points. 62 | window_size (int): The embedding dimension, representing the length of templates. 63 | r (float): The tolerance parameter that defines the maximum difference allowed between data points of two templates to consider them as a match. 64 | 65 | Returns: 66 | float: The calculated Sample Entropy value. 67 | 68 | ''' 69 | 70 | B = get_matches(construct_templates(timeseries_data, window_size), r) 71 | A = get_matches(construct_templates(timeseries_data, window_size+1), r); 72 | return -log(A/B); -------------------------------------------------------------------------------- /Legacy/sp500_data.py: -------------------------------------------------------------------------------- 1 | from tradier import * 2 | 3 | 4 | sp500 = pd.read_csv('https://raw.githubusercontent.com/datasets/s-and-p-500-companies/main/data/constituents.csv'); 5 | 6 | 7 | sp500 = sp500.sample(15); 8 | 9 | sp500_symbols = list(sp500['Symbol']); 10 | 11 | df = pd.DataFrame({'profitable':[], 'volume_change':[]}); 12 | 13 | for s in sp500_symbols: 14 | bar_data = quotes.get_historical_quotes(s, interval='monthly', start_date='2022-10-01'); 15 | print(bar_data) 16 | bar_data['price_change'] = bar_data['close'] - bar_data['open']; 17 | bar_data['profitable'] = (bar_data['close']-bar_data['open'] > 0).astype(int); 18 | bar_data['volume_change'] = bar_data['volume'].diff(); 19 | 20 | df = df.append(bar_data[['profitable', 'volume_change']]); 21 | 22 | 23 | -------------------------------------------------------------------------------- /Legacy/straddle_orders.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | # 4 | # Implement straddle option strategy (long/short) 5 | # 6 | 7 | def straddle_order (symbol, option0, quantity0, option1, quantity1, side, duration='day'): 8 | ''' 9 | Place a long-straddle or short-straddle order on the given underlying symbol. 10 | 11 | Args: 12 | symbol: underlying asset of option contracts 13 | option0: call leg of the straddle 14 | quantity0: number of call contracts 15 | option1: put leg of the straddle 16 | quantity1: number of put contracts 17 | side: ['long', 'short'] 18 | duration: day, gtc, pre, post 19 | 20 | Returns: 21 | json response to confirm success. 22 | 23 | Example: 24 | # determine the current price of a given underlying 25 | last_price = get_quote_day('UDR', last_price=True) 26 | 27 | option0 = option_chain_day('UDR', strike_low=40, strike_high=40, option_type='call')['symbol'] 28 | option1 = option_chain_day('UDR', strike_low=40, strike_high=40, option_type='put')['symbol'] 29 | 30 | straddle_order(symbol='UDR', option0=option0, quantity0=1.0, option1=option1, quantity1=1.0, side='long', duration='gtc') 31 | {'order': {'id': 7780584, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 32 | ''' 33 | 34 | # 35 | # Check that trade_side is either long (long straddle) or short (short straddle) 36 | # 37 | 38 | trade_side = ''; 39 | 40 | if side.lower() not in ['long', 'short']: 41 | return 'Need the side to be either "long" or "short"'; 42 | 43 | if side.lower() == 'long': 44 | trade_side = 'buy_to_open'; 45 | if side.lower() == 'short': 46 | trade_side = 'sell_to_open'; 47 | 48 | r = requests.post( 49 | url = '{}/{}'.format(SANDBOX_URL, ORDER_ENDPOINT), 50 | data = { 51 | 'class' : 'multileg', 52 | 'symbol' : symbol, 53 | 'type' : 'market', 54 | 'duration' : duration, 55 | 'option_symbol[0]' : option0, 56 | 'side[0]' : trade_side, 57 | 'quantity[0]' : quantity0, 58 | 'option_symbol[1]' : option1, 59 | 'side[1]' : trade_side, 60 | 'quantity[1]' : quantity1 61 | }, 62 | headers = REQUESTS_HEADERS 63 | ); 64 | 65 | return r.json(); -------------------------------------------------------------------------------- /Legacy/tradier_examples.py: -------------------------------------------------------------------------------- 1 | from tradier import * 2 | 3 | 4 | 5 | dotenv.load_dotenv(); 6 | 7 | 8 | # 9 | # Fetch account credentials from .env file 10 | # 11 | 12 | ACCOUNT_NUMBER = os.getenv('tradier_acct'); 13 | AUTH_TOKEN = os.getenv('tradier_token'); 14 | 15 | 16 | 17 | # 18 | # Instantiate class-based objects 19 | # 20 | 21 | account = Account(ACCOUNT_NUMBER, AUTH_TOKEN); 22 | quotes = Quotes(ACCOUNT_NUMBER, AUTH_TOKEN); 23 | options = OptionsData(ACCOUNT_NUMBER, AUTH_TOKEN); 24 | 25 | options_order = OptionsOrder(ACCOUNT_NUMBER, AUTH_TOKEN); 26 | equity_order = EquityOrder(ACCOUNT_NUMBER, AUTH_TOKEN); 27 | 28 | 29 | 30 | # 31 | # Fetch current bar data about Molson Coors Beverage Co ($TAP) 32 | # 33 | 34 | df_tap = quotes.get_quote_day('TAP'); 35 | 36 | 37 | # 38 | # Place market order to buy 5 shares of TAP 39 | # 40 | 41 | equity_order.order(symbol='TAP', side='buy', quantity=5, order_type='market', duration='gtc'); # gtc = good till cancelled 42 | 43 | 44 | 45 | # 46 | # Place market order to long 1 put option contract for TAP 47 | # 48 | 49 | # Determine the OCC symbol to transact because will need as input to options order method 50 | put_contract_symbol = options.get_chain_day(symbol='TAP', strike=60, option_type='put')['symbol']; 51 | 52 | 53 | # Call OptionsOrder.options_order method to execute buy market order 54 | # 55 | # Successful order will return JSON 56 | # {'order': {'id': 8382667, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 57 | 58 | options_order.options_order(occ_symbol=put_contract_symbol, order_type='market', underlying='TAP', side='buy_to_open', quantity=1, duration='gtc'); 59 | 60 | 61 | 62 | # 63 | # View assets currently in portfolio 64 | # 65 | 66 | account.get_positions(); 67 | 68 | 69 | 70 | # 71 | # Check if there are pending orders 72 | # 73 | 74 | account.get_orders(); 75 | 76 | 77 | 78 | 79 | 80 | 81 | # 82 | # Bear Call Spread 83 | # 84 | 85 | # for example, if hban = $10.70, then let S(t) = 10.70 ; K1 = 12.50 ; K2 = 15.0 86 | 87 | hban = quotes.get_quote_day('HBAN', last_price=True); 88 | 89 | # Define the short call and long call legs of the spread 90 | leg0 = options.get_chain_day('HBAN', strike=12.50, option_type='call')['symbol']; 91 | leg1 = options.get_chain_day('HBAN', strike=15.00, option_type='call')['symbol']; 92 | 93 | # Successful options order will return JSON with the following format 94 | # {'order': {'id': 8028987, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 95 | options_order.bear_call_spread(underlying='HBAN', option0=leg0, quantity0=1.0, option1=leg1, quantity1=1.0, duration='gtc'); 96 | -------------------------------------------------------------------------------- /Legacy/tradierinfo.txt: -------------------------------------------------------------------------------- 1 | CURRENT LIST OF FUNCTIONS 2 | 3 | 1. get_profile () 4 | 5 | 6 | 2. 7 | get_historical_quotes ( 8 | symbol, interval='daily', start_date='', end_date=datetime.now().strftime('%Y-%m-%d') 9 | ) 10 | 11 | 12 | 3. option_chain_day (symbol, expiry='', strike_low=False, strike_high=False) 13 | 14 | 4. get_expiry_dates (symbol, strikes=False) 15 | 16 | 5. get_quote_day (symbol) 17 | 18 | 6. get_positions() 19 | 20 | 7. get_option_symbols (underlying_symbol, df=False), symbol_list_to_df (option_list), parse_option_expiries (expiry_list) -------------------------------------------------------------------------------- /Legacy/tradiersetup.txt: -------------------------------------------------------------------------------- 1 | VAGUE OUTLINE OF HOW TO SETUP TRADIER 2 | 3 | 1. Create tradier account at tradier.com 4 | 5 | 2. Create folder on computer where tradier files will exist 6 | 7 | 3. Get Sandbox Account Access credentials from Tradier dashboard (Account Number, Access Token) 8 | 3a. Settings > "API Access" > {Account Number, Access Token} 9 | 10 | 4. Add tradier credentials to .env folder in tradier directory 11 | 3a. tradier_account = '....' 12 | 3b. tradier_token = '...' 13 | 14 | 5. Create config.py file 15 | Example: https://github.com/thammo4/tradier/blob/main/config.py 16 | 5a. import necessary modules 17 | 5b. define .env credentials (i.e. ACCOUNT_NUMBER and AUTH_TOKEN) 18 | 5c. define url endpoints 19 | 20 | 6. Test setup by fetching account information 21 | Example: https://github.com/thammo4/tradier/blob/main/getuser.py 22 | 23 | 7. $$$$$$ -------------------------------------------------------------------------------- /Legacy/uvatradier/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Legacy/uvatradier/README.md: -------------------------------------------------------------------------------- 1 | # tradier 2 | 3 | Python wrapper for the tradier brokerage API 4 | 5 | Class based functionality currently in development. Code is found in tradier.py 6 | 7 | 8 | To get tradier.py to work, need to have 'tradier_acct' and 'tradier_token' defined in a .env file in the same directory. 9 | 10 | From a python file / jupyter notebook, just run `from tradier import *` 11 | -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | from .account import Account 3 | from .quotes import Quotes 4 | from .equity_order import EquityOrder 5 | from .options_data import OptionsData 6 | from .options_order import OptionsOrder 7 | 8 | print('hello, world!') -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/account.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import pandas as pd 3 | 4 | from .base import Tradier 5 | 6 | class Account (Tradier): 7 | def __init__ (self, account_number, auth_token): 8 | Tradier.__init__(self, account_number, auth_token); 9 | 10 | # 11 | # Account endpoints 12 | # 13 | 14 | self.PROFILE_ENDPOINT = "v1/user/profile"; # GET 15 | 16 | self.POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(account_number); # GET 17 | 18 | 19 | self.ACCOUNT_BALANCE_ENDPOINT = "v1/accounts/{}/balances".format(account_number); # GET 20 | self.ACCOUNT_GAINLOSS_ENDPOINT = "v1/accounts/{}/gainloss".format(account_number); # GET 21 | self.ACCOUNT_HISTORY_ENDPOINT = "v1/accounts/{}/history".format(account_number); # GET 22 | self.ACCOUNT_POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(account_number); # GET 23 | 24 | self.ACCOUNT_INDIVIDUAL_ORDER_ENDPOINT = "v1/accounts/{account_id}/orders/{order_id}"; # GET 25 | 26 | 27 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(account_number); # GET 28 | 29 | def get_user_profile(self): 30 | ''' 31 | Fetch the user profile information from the Tradier Account API. 32 | 33 | This function makes a GET request to the Tradier Account API to retrieve the user profile 34 | information associated with the trading account linked to the provided credentials. 35 | The API response is expected to be in JSON format, containing details about the user profile. 36 | 37 | Returns: 38 | pandas.DataFrame: A DataFrame containing user profile information. 39 | 40 | Example: 41 | # Retrieve user profile information 42 | user_profile = get_user_profile() 43 | transposed_profile = user_profile.T # Transpose the DataFrame for easy viewing. 44 | 45 | Example DataFrame (transposed): 46 | 47 | id id-sb-2r01lpprbg 48 | name Fat Albert 49 | account.account_number ABC1234567 50 | account.classification individual 51 | account.date_created 2021-06-23T22:04:20.000Z 52 | account.day_trader False 53 | account.option_level 6 54 | account.status active 55 | account.type margin 56 | account.last_update_date 2021-06-23T22:04:20.000Z 57 | ''' 58 | r = requests.get( 59 | url = '{}/{}'.format(self.SANDBOX_URL, self.PROFILE_ENDPOINT), 60 | params = {}, 61 | headers = self.REQUESTS_HEADERS 62 | ); 63 | 64 | return pd.json_normalize(r.json()['profile']); 65 | 66 | 67 | def get_account_balance(self): 68 | ''' 69 | Fetch the account balance information from the Tradier Account API. 70 | 71 | This function makes a GET request to the Tradier Account API to retrieve the account 72 | balance information for the trading account associated with the provided credentials. 73 | The API response is expected to be in JSON format, containing details about the account 74 | balance. 75 | 76 | Returns: 77 | pandas.DataFrame: A DataFrame containing account balance information. 78 | 79 | Example: 80 | # Retrieve account balance information 81 | account_balance = get_account_balance() 82 | transposed_balance = account_balance.T # Transpose the DataFrame for easy viewing. 83 | 84 | Example DataFrame (transposed): 85 | 0 86 | option_short_value -147.0 87 | total_equity 74314.82 88 | account_number ABC1234567 89 | account_type margin 90 | close_pl 0 91 | current_requirement 17595.08 92 | equity 0 93 | long_market_value 34244.16 94 | market_value 34097.16 95 | open_pl -225300.265 96 | option_long_value 1054.0 97 | option_requirement 1000.0 98 | pending_orders_count 8 99 | short_market_value -147.0 100 | stock_long_value 33190.16 101 | total_cash 40217.66 102 | uncleared_funds 0 103 | pending_cash 16892.9 104 | margin.fed_call 0 105 | margin.maintenance_call 0 106 | margin.option_buying_power 38919.84 107 | margin.stock_buying_power 77839.68 108 | margin.stock_short_value 0 109 | margin.sweep 0 110 | ''' 111 | r = requests.get( 112 | url = '{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_BALANCE_ENDPOINT), 113 | params = {}, 114 | headers = self.REQUESTS_HEADERS 115 | ); 116 | 117 | return pd.json_normalize(r.json()['balances']); 118 | 119 | 120 | def get_gainloss (self): 121 | ''' 122 | Get cost basis information for a specific user account. 123 | This includes information for all closed positions. 124 | Cost basis information is updated through a nightly batch reconciliation process with tradier clearing firm. 125 | 126 | Returns: 127 | Pandas dataframe with columns [close_date, cost, gain_loss, gain_loss_percent, open_date, proceeds, quantity, symbol, term] 128 | 129 | Example: 130 | >>> account.get_gainloss().head() 131 | close_date cost gain_loss gain_loss_percent open_date proceeds quantity symbol term 132 | 0 2023-09-13T00:00:00.000Z 194700.0 -30600.0 -15.72 2023-08-25T00:00:00.000Z 164100.0 10.0 LMT240119C00260000 19 133 | 1 2023-09-13T00:00:00.000Z 10212.2 -432.6 -4.24 2023-09-06T00:00:00.000Z 9779.6 20.0 KLAC 7 134 | 2 2023-09-13T00:00:00.000Z 2300.0 175.0 7.61 2023-08-24T00:00:00.000Z 2475.0 1.0 HAL251219C00018000 20 135 | 3 2023-09-13T00:00:00.000Z 20700.0 1620.0 7.83 2023-08-24T00:00:00.000Z 22320.0 9.0 HAL251219C00018000 20 136 | 4 2023-09-06T00:00:00.000Z 16967.0 -193.0 -1.14 2023-09-01T00:00:00.000Z 16774.0 100.0 TXN 5 137 | ''' 138 | r = requests.get( 139 | url = '{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_GAINLOSS_ENDPOINT), 140 | params = {}, 141 | headers = self.REQUESTS_HEADERS 142 | ); 143 | 144 | return pd.json_normalize(r.json()['gainloss']['closed_position']); 145 | 146 | 147 | def get_orders (self): 148 | ''' 149 | This function returns a pandas DataFrame. 150 | Each row denotes a queued order. Each column contiains a feature_variable pertaining to the order. 151 | Transposed sample output has the following structure: 152 | 153 | >>> account.get_orders().T 154 | 0 1 155 | id 8248093 8255194 156 | type stop_limit market 157 | symbol UNP CF 158 | side buy buy 159 | quantity 3.0 10.0 160 | status open filled 161 | duration day gtc 162 | price 200.0 NaN 163 | avg_fill_price 0.0 87.39 164 | exec_quantity 0.0 10.0 165 | last_fill_price 0.0 87.39 166 | last_fill_quantity 0.0 10.0 167 | remaining_quantity 3.0 0.0 168 | stop_price 200.0 NaN 169 | create_date 2023-09-25T20:29:10.351Z 2023-09-26T14:45:00.155Z 170 | transaction_date 2023-09-26T12:30:19.152Z 2023-09-26T14:45:00.216Z 171 | class equity equity 172 | ''' 173 | 174 | r = requests.get( 175 | url='{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 176 | params={'includeTags':'true'}, 177 | headers=self.REQUESTS_HEADERS 178 | ); 179 | 180 | # return pd.DataFrame(r.json()['orders']); 181 | return pd.json_normalize(r.json()['orders']['order']); 182 | 183 | 184 | def get_positions(self, symbols=False, equities=False, options=False): 185 | ''' 186 | Fetch and filter position data from the Tradier Account API. 187 | 188 | This function makes a GET request to the Tradier Account API to retrieve position 189 | information related to a trading account. The API response is expected to be in 190 | JSON format, containing details about the positions held in the account. 191 | 192 | Args: 193 | symbols (list, optional): A list of trading symbols (e.g., stock ticker symbols) 194 | to filter the position data. If provided, only positions 195 | matching these symbols will be included. 196 | equities (bool, optional): If True, filter the positions to include only equities 197 | (stocks) with symbols less than 5 characters in length. 198 | If False, no filtering based on equities will be applied. 199 | options (bool, optional): If True, filter the positions to include only options 200 | with symbols exceeding 5 characters in length. 201 | If False, no filtering based on options will be applied. 202 | 203 | Returns: 204 | pandas.DataFrame: A DataFrame containing filtered position information based on 205 | the specified criteria. 206 | 207 | Example: 208 | # Retrieve all positions without filtering 209 | all_positions = get_positions() 210 | 211 | # Retrieve positions for specific symbols ('AAPL', 'GOOGL') 212 | specific_positions = get_positions(symbols=['AAPL', 'GOOGL']) 213 | 214 | # Retrieve only equities 215 | equities_positions = get_positions(equities=True) 216 | 217 | # Retrieve only options 218 | options_positions = get_positions(options=True) 219 | ''' 220 | r = requests.get(url='{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_POSITIONS_ENDPOINT), params={}, headers=self.REQUESTS_HEADERS); 221 | if r.json(): 222 | positions_df = pd.DataFrame(r.json()['positions']['position']); 223 | if symbols: 224 | positions_df = positions_df.query('symbol in @symbols'); 225 | if equities: 226 | positions_df = positions_df[positions_df['symbol'].str.len() < 5]; 227 | options = False; 228 | if options: 229 | positions_df = positions_df[positions_df['symbol'].str.len() > 5]; 230 | return positions_df; 231 | -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/base.py: -------------------------------------------------------------------------------- 1 | class Tradier: 2 | def __init__ (self, account_number, auth_token): 3 | 4 | # 5 | # Define account credentials 6 | # 7 | 8 | self.ACCOUNT_NUMBER = account_number; 9 | self.AUTH_TOKEN = auth_token; 10 | self.REQUESTS_HEADERS = {'Authorization':'Bearer {}'.format(self.AUTH_TOKEN), 'Accept':'application/json'} 11 | 12 | 13 | # 14 | # Define base url for paper trading and individual API endpoints 15 | # 16 | 17 | self.SANDBOX_URL = 'https://sandbox.tradier.com'; -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/equity_order.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | 4 | class EquityOrder (Tradier): 5 | def __init__ (self, account_number, auth_token): 6 | Tradier.__init__(self, account_number, auth_token); 7 | 8 | # 9 | # Order endpoint 10 | # 11 | 12 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(self.ACCOUNT_NUMBER); # POST 13 | 14 | def order (self, symbol, side, quantity, order_type, duration='day', limit_price=False, stop_price=False): 15 | ''' 16 | Example of how to run: 17 | >>> eo = EquityOrder(ACCOUNT_NUMBER, AUTH_TOKEN) 18 | >>> eo.order(symbol='QQQ', side='buy', quantity=10, order_type='market', duration='gtc'); 19 | {'order': {'id': 8256590, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 20 | ''' 21 | 22 | # 23 | # Define initial requests parameters dictionary whose fields are applicable to all order_type values 24 | # 25 | 26 | r_params = { 27 | 'class' : 'equity', 28 | 'symbol' : symbol, 29 | 'side' : side, 30 | 'quantity' : quantity, 31 | 'type' : order_type, 32 | 'duration' : duration 33 | }; 34 | 35 | # 36 | # If the order_type is limit, stop, or stop_limit --> Set the appropriate limit price or stop price 37 | # 38 | 39 | if order_type.lower() in ['limit', 'stop_limit']: 40 | r_params['price'] = limit_price; 41 | if order_type.lower() in ['stop', 'stop_limit']: 42 | r_params['stop'] = stop_price; 43 | 44 | r = requests.post( 45 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 46 | params = r_params, 47 | headers=self.REQUESTS_HEADERS 48 | ); 49 | 50 | return r.json(); -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/options_data.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | class OptionsData (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Option data endpoints 9 | # 10 | 11 | self. OPTIONS_STRIKE_ENDPOINT = "v1/markets/options/strikes"; # GET 12 | self. OPTIONS_CHAIN_ENDPOINT = "v1/markets/options/chains"; # GET 13 | self. OPTIONS_EXPIRY_ENDPOINT = "v1/markets/options/expirations"; # GET 14 | self. OPTIONS_SYMBOL_ENDPOINT = "v1/markets/options/lookup"; # GET 15 | 16 | 17 | # 18 | # Fetch all option chain data for a single day of contract expirations 19 | # 20 | 21 | def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False): 22 | ''' 23 | This function returns option chain data for a given symbol. 24 | All contract expirations occur on the same expiry date 25 | ''' 26 | 27 | # 28 | # Set the contract expiration date to the nearest valid date 29 | # 30 | 31 | if not expiry: 32 | expiry = self.get_expiry_dates(symbol)[0]; 33 | 34 | # 35 | # Define request object for given symbol and expiration 36 | # 37 | 38 | r = requests.get( 39 | url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_CHAIN_ENDPOINT), 40 | params = {'symbol':symbol, 'expiration':expiry, 'greeks':'false'}, 41 | headers = self.REQUESTS_HEADERS 42 | ); 43 | 44 | # 45 | # Convert returned json -> pandas dataframe 46 | # 47 | 48 | option_df = pd.DataFrame(r.json()['options']['option']); 49 | 50 | 51 | # 52 | # Remove columns which have the same value for every row 53 | # 54 | 55 | cols_to_drop = option_df.nunique()[option_df.nunique() == 1].index; 56 | option_df = option_df.drop(cols_to_drop, axis=1); 57 | 58 | # 59 | # Remove columns which have NaN in every row 60 | # 61 | 62 | cols_to_drop = option_df.nunique()[option_df.nunique() == 0].index; 63 | option_df = option_df.drop(cols_to_drop, axis=1); 64 | 65 | 66 | # 67 | # Remove description column because its information is redundant 68 | # 69 | 70 | cols_to_drop = ['description']; 71 | option_df = option_df.drop(cols_to_drop, axis=1); 72 | 73 | 74 | # 75 | # Filter rows per strike_low and strike_high 76 | # 77 | 78 | if strike_low: 79 | option_df = option_df.query('strike >= @strike_low'); 80 | 81 | if strike_high: 82 | option_df = option_df.query('strike <= @strike_high'); 83 | 84 | if strike: 85 | option_df = option_df.query('strike == @strike'); 86 | 87 | if option_type in ['call', 'put']: 88 | option_df = option_df.query('option_type == @option_type'); 89 | 90 | 91 | if option_type: 92 | if option_type in ['call', 'put']: 93 | option_df = option_df.query('option_type == @option_type'); 94 | 95 | # 96 | # Return the resulting dataframe whose rows are individual contracts with expiration `expiry` 97 | # 98 | 99 | return option_df; 100 | 101 | 102 | def get_expiry_dates (self, symbol, strikes=False): 103 | ''' 104 | Get the expiry dates for options on a given symbol. 105 | 106 | Args: 107 | symbol (str): The symbol for which to retrieve expiry dates. 108 | strikes (bool, optional): Whether to include strike prices for each expiry date. Defaults to False. 109 | 110 | Returns: 111 | If strikes=False -> returns list or list of dict: A list of expiry dates in the format 'YYYY-MM-DD'. 112 | If strikes=True -> returns a list of dictionaries with expiry date and associated strike prices. 113 | 114 | Example: 115 | >>> options = OptionsData(ACCOUNT_NUMBER, AUTH_TOKEN) 116 | >>> options.get_expiry_dates(symbol='DFS') 117 | ['2023-09-08', '2023-09-15', '2023-09-22', '2023-09-29', '2023-10-06', '2023-10-13', '2023-10-20', '2023-11-17', '2024-01-19', '2024-04-19', '2024-06-21', '2025-01-17'] 118 | 119 | >>> options.get_expiry_dates(symbol='DFS', strikes=True) 120 | [{'date': '2023-09-08', 'strikes': {'strike': [59.0, 60.0, 61.0, ...]}, {'date': '2023-09-15', 'strikes': {'strike': [55.0, 60.0, ...}}, ...] 121 | ''' 122 | 123 | r = requests.get( 124 | url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_EXPIRY_ENDPOINT), 125 | params = {'symbol':symbol, 'includeAllRoots':True, 'strikes':str(strikes)}, 126 | headers = self.REQUESTS_HEADERS 127 | ); 128 | 129 | if r.status_code != 200: 130 | return 'wtf'; 131 | 132 | expiry_dict = r.json()['expirations']; 133 | 134 | # Without strikes, we can get a list of dates 135 | if strikes == False: 136 | return expiry_dict['date']; 137 | 138 | # Otherwise, return a list whose elements are dicts with format {'date':[list_of_strikes]} 139 | return expiry_dict['expiration']; 140 | 141 | 142 | # 143 | # This function returns a list of symbols. Each element has a format 144 | # COP240216C00115000 145 | # 146 | # i.e. 147 | # 148 | # COP 240216 C 00115000 149 | # 150 | 151 | # ########################## 152 | # TODO (9/27): 153 | # 1. Change get_options_symbols so that it returns the dataframe format by default 154 | # 2. Investigate the KeyError: 'expiration_date' error produced from `options.get_options_symbols('BWA', df=True)` 155 | # • Issue arises because there are two different symbols for BorgWarner Inc. {'BWA', 'BWA1'} 156 | # • Not sure how to fix this problem yet though 157 | # ########################## 158 | 159 | def get_options_symbols (self, symbol, df=False): 160 | ''' 161 | This function provides a convenient wrapper to fetch option symbols 162 | for a specified symbol 163 | 164 | If df=False, then a list of OCC options symbols is returned. 165 | For an n-element list, if i=0,...,(n-1), then every (option_list[i-1], option_list[i]) pair of elements represents a pair of call/put options. 166 | 167 | If df=True, then a pandas.DataFrame object is returned. 168 | Each row of the dataframe represents a single put or call contract. 169 | The first column is the OCC symbol. The subsequent columns are the parsed values of the OCC symbol. 170 | ''' 171 | 172 | # 173 | # Helper function to convert the get_option_symbols list into a dataframe 174 | # 175 | 176 | def symbol_list_to_df (option_list): 177 | ''' 178 | This is a helper function called from get_option_symbols. 179 | It will parse a list of option symbols into their constituent components 180 | For example: 181 | option_symbol 182 | = [underlying_symbol, expiry_date, option_type, option_id] 183 | = [LMT, 240119, C, 00300000] 184 | ''' 185 | parsed_options = [] 186 | 187 | for option in option_list: 188 | match = re.match(r'([A-Z]+)(\d{6})([CP])(\d+)', option) 189 | if match: 190 | root_symbol, expiration_date, option_type, strike_price = match.groups() 191 | parsed_options.append({'symbol':option,'root_symbol':root_symbol, 'expiration_date':expiration_date, 'option_type':option_type, 'strike_price':strike_price}) 192 | return pd.DataFrame(parsed_options); 193 | 194 | 195 | # 196 | # Helper function to turn the option dates into standard date format 197 | # 198 | def parse_option_expiries (expiry_list): 199 | ''' 200 | Helper function to turn the option dates into standard date format 201 | ''' 202 | 203 | formatted_expiries = []; 204 | for x in expiry_list: 205 | formatted_expiries.append(datetime.strptime(x, '%y%m%d').strftime('%Y-%m-%d')); 206 | 207 | return formatted_expiries; 208 | 209 | 210 | r = requests.get( 211 | url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_SYMBOL_ENDPOINT), 212 | params = {'underlying':symbol}, 213 | headers = self.REQUESTS_HEADERS 214 | ); 215 | 216 | option_list = r.json()['symbols'][0]['options']; 217 | 218 | if df: 219 | option_df = symbol_list_to_df(option_list); 220 | option_df['expiration_date'] = parse_option_expiries(option_df['expiration_date']); 221 | return option_df; 222 | 223 | return option_list; -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/options_order.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | class OptionsOrder (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Order endpoint 9 | # 10 | 11 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(ACCOUNT_NUMBER); # POST 12 | 13 | 14 | # 15 | # Bear-put spread 16 | # 17 | 18 | def bear_put_spread (self, underlying, option0, quantity0, option1, quantity1, duration='day'): 19 | ''' 20 | Parameters 21 | underlying: asset symbol (e.g. 'SPY') 22 | option0: OCC symbol of 23 | ''' 24 | r = requests.post( 25 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 26 | data = { 27 | 'class' : 'multileg', 28 | 'symbol' : underlying, 29 | 'type' : 'market', 30 | 'duration' : duration, 31 | 'option_symbol[0]' : option0, 32 | 'side[0]' : 'buy_to_open', 33 | 'quantity[0]' : quantity0, 34 | 'option_symbol[1]' : option1, 35 | 'side[1]' : 'sell_to_open', 36 | 'quantity[1]' : quantity1 37 | }, 38 | headers = self.REQUESTS_HEADERS 39 | ); 40 | 41 | return r.json(); 42 | 43 | 44 | # 45 | # Bear-call spread 46 | # 47 | 48 | def bear_call_spread (self, underlying, option0, quantity0, option1, quantity1, duration='day'): 49 | ''' 50 | Bear call spread example: 51 | • XYZ @ $50/share 52 | • Pr(XYZ < $55/share) > .50 53 | • Legs 54 | • Short Call with K1 ≥ S (e.g. K1=55 > S=50) and receive $3 premium 55 | • Long Call with K2 > K1 ≥ S (e.g. K2=60 > K1=55 ≥ S=50) and pay $1 premium 56 | • Expiry t=T 57 | • If S(T) < K1 -> payoff = premium differential 58 | • If K1 < S(T) < K2 59 | • short call exercised and must sell at K1 = $55 60 | • long call expires OTM 61 | • payoff = (K1-K2) + (premium differential) < 0 62 | • If S(T) > K2 > K1 63 | • short call exercised and must sell at K1 = $55 64 | • long call exercised and can buy XYZ at K2 = $60 65 | • payoff = (K1-K2) + (premium differential) < 0 66 | ''' 67 | r = requests.post( 68 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 69 | data = { 70 | 'class' : 'multileg', 71 | 'symbol' : underlying, 72 | 'type' : 'market', 73 | 'duration' : duration, 74 | 'option_symbol[0]' : option0, 75 | 'side[0]' : 'buy_to_open', 76 | 'quantity[0]' : quantity0, 77 | 'option_symbol[1]' : option1, 78 | 'side[1]' : 'sell_to_open', 79 | 'quantity[1]' : quantity1 80 | }, 81 | headers = self.REQUESTS_HEADERS 82 | ); 83 | 84 | return r.json(); 85 | 86 | 87 | 88 | # 89 | # Bull-put spread 90 | # 91 | 92 | def bull_put_spread (self, underlying_symbol, option_symbol_0, quantity_0, option_symbol_1, quantity_1, duration='day'): 93 | r = requests.post( 94 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 95 | data = { 96 | 'class' : 'multileg', 97 | 'symbol' : underlying_symbol, 98 | 'type' : 'market', 99 | 'duration' : duration, 100 | 'option_symbol[0]' : option_symbol_0, 101 | 'side[0]' : 'sell_to_open', 102 | 'quantity[0]' : quantity_0, 103 | 'option_symbol[1]' : option_symbol_1, 104 | 'side[1]' : 'buy_to_open', 105 | 'quantity[1]' : quantity_1 106 | }, 107 | headers = self.REQUESTS_HEADERS 108 | ); 109 | 110 | return r.json(); 111 | 112 | # 113 | # Bull-call spread 114 | # 115 | 116 | def bull_call_spread (self, underlying_symbol, option_symbol_0, quantity_0, option_symbol_1, quantity_1, duration='day'): 117 | r = requests.post( 118 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 119 | data = { 120 | 'class' : 'multileg', 121 | 'symbol' : underlying_symbol, 122 | 'type' : 'market', 123 | 'duration' : duration, 124 | 'option_symbol[0]' : option_symbol_0, 125 | 'side[0]' : 'buy_to_open', 126 | 'quantity[0]' : quantity_0, 127 | 'option_symbol[1]' : option_symbol_1, 128 | 'side[1]' : 'sell_to_open', 129 | 'quantity[1]' : quantity_1 130 | }, 131 | headers = self.REQUESTS_HEADERS 132 | ); 133 | 134 | return r.json(); 135 | 136 | 137 | # def extract_stock_symbol (self, occ_symbol): 138 | def extract_occ_underlying (self, occ_symbol): 139 | match = re.match(r'^([A-Z]){1,4}\d', occ_symbol); 140 | if match: 141 | return match.group(1); 142 | else: 143 | return None; 144 | 145 | 146 | def options_order (self, occ_symbol, order_type, side, quantity, underlying=False, limit_price=False, stop_price=False, duration='day'): 147 | ''' 148 | Params: 149 | • occ_symbol = options contract (e.g. 'TER230915C00110000') 150 | • order_type = The type of order to be placed. One of: market, limit, stop, stop_limit 151 | • side = The side of the order. One of: buy_to_open, buy_to_close, sell_to_open, sell_to_close 152 | • quantity = Number of contracts to buy/sell 153 | • underlying = Underlying symbol. If not supplied, will be inferred from occ_symbol. (e.g. 'TER') 154 | • duration = Time the order will remain active. One of: day, gtc, pre, post 155 | • limit_price = Limit Price for limit or stop_limit orders 156 | • stop_price = Stop Price for stop or stop_limit orders 157 | 158 | Returns: 159 | • json from the requests.post call to the order endpoint 160 | 161 | Notes: 162 | • If order_type='limit' or order_type = 'stop_limit', then must specify limit_price 163 | • If order_type='stop' or order_type = 'stop_limit', then must specify stop_price 164 | 165 | Example: 166 | >>> options_order.options_order(occ_symbol='LMT240119C00260000', order_type='market', side='sell_to_close', quantity=10.0) 167 | # Returns: {'order': {'id': 8042606, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 168 | ''' 169 | if not underlying: 170 | underlying = self.extract_occ_underlying(occ_symbol); 171 | 172 | r_data = { 173 | 'class' : 'option', 174 | 'symbol' : underlying, 175 | 'option_symbol' : occ_symbol, 176 | 'side' : side, 177 | 'quantity' : quantity, 178 | 'type' : order_type, 179 | 'duration' : duration 180 | }; 181 | 182 | if order_type in ['limit', 'stop_limit']: 183 | if not limit_price: 184 | print('Need limit price.'); 185 | return None; 186 | r_data['price'] = limit_price; 187 | if order_type in ['stop', 'stop_limit']: 188 | if not stop_price: 189 | print('Need stop price.'); 190 | return None; 191 | r_data['stop'] = stop_price; 192 | 193 | r = requests.post( 194 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 195 | data = r_data, 196 | headers = self.REQUESTS_HEADERS 197 | ); 198 | 199 | return r.json(); -------------------------------------------------------------------------------- /Legacy/uvatradier/build/lib/uvatradier/quotes.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | class Quotes (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Quotes endpoints for market data about equities 9 | # 10 | 11 | self.QUOTES_ENDPOINT = "v1/markets/quotes"; # GET (POST) 12 | self.QUOTES_HISTORICAL_ENDPOINT = "v1/markets/history"; # GET 13 | self.QUOTES_SEARCH_ENDPOINT = "v1/markets/search"; # GET 14 | self.QUOTES_TIMESALES_ENDPOINT = "v1/markets/timesales"; # GET 15 | 16 | def get_historical_quotes (self, symbol, interval='daily', start_date=False, end_date=False): 17 | 18 | ''' 19 | Fetch historical stock data for a given symbol from the Tradier Account API. 20 | 21 | This function makes a GET request to the Tradier Account API to retrieve historical stock 22 | data for a specified symbol within a specified time interval. 23 | 24 | Args: 25 | symbol (str): The trading symbol of the stock (e.g., 'AAPL', 'MSFT') for which you want 26 | to retrieve historical data. 27 | interval (str, optional): The time interval for historical data. Default is 'daily'. Alt values are 'weekly' or 'monthly'. 28 | start_date (str, optional): The start date for historical data in the format 'YYYY-MM-DD'. 29 | If not provided, the function will default to the most recent Monday. 30 | end_date (str, optional): The end date for historical data in the format 'YYYY-MM-DD'. 31 | If not provided, the function will default to the current date. 32 | 33 | Returns: 34 | pandas.DataFrame: A DataFrame containing historical stock data for the specified symbol. 35 | 36 | Example: 37 | # Create a Quotes instance 38 | q = Quotes(ACCOUNT_NUMBER, AUTH_TOKEN) 39 | 40 | # Retrieve historical stock data for symbol 'BIIB' 41 | historical_data = q.get_historical_quotes(symbol='BIIB') 42 | 43 | Sample Output: 44 | date open high low close volume 45 | 0 2023-08-28 265.40 266.470 263.54 265.05 359872 46 | 1 2023-08-29 265.35 268.150 265.11 268.00 524972 47 | 2 2023-08-30 268.84 269.460 265.25 267.18 552728 48 | 3 2023-08-31 266.83 269.175 265.32 267.36 1012842 49 | 4 2023-09-01 269.01 269.720 266.91 267.17 522401 50 | ''' 51 | 52 | # 53 | # Helper function used to index the start of the trading week 54 | # 55 | 56 | def last_monday (input_date): 57 | ''' 58 | Find the date of the previous Monday for a given input date. 59 | 60 | Args: 61 | input_date (datetime.date): the input date 62 | 63 | Returns: 64 | datetime.date: The date of the previous Monday. 65 | ''' 66 | 67 | return (input_date - timedelta(days=(input_date.weekday()))); 68 | 69 | if not end_date: 70 | end_date = datetime.today().strftime('%Y-%m-%d'); 71 | 72 | if not start_date: 73 | tmp = datetime.strptime(end_date, '%Y-%m-%d'); 74 | start_date = last_monday(tmp).strftime('%Y-%m-%d'); 75 | 76 | r = requests.get( 77 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_HISTORICAL_ENDPOINT), 78 | params = { 79 | 'symbol' : symbol, 80 | 'interval' : interval, 81 | 'start' : start_date, 82 | 'end' : end_date 83 | }, 84 | headers = self.REQUESTS_HEADERS 85 | ); 86 | 87 | return pd.DataFrame(r.json()['history']['day']); 88 | 89 | def get_quote_day (self, symbol, last_price=False): 90 | ''' 91 | Fetch the current quote data for a given symbol from the Tradier Account API. 92 | 93 | This function makes a GET request to the Tradier Account API to retrieve the current quote 94 | data for a specified symbol. 95 | 96 | Args: 97 | symbol (str): The trading symbol of the stock (e.g., 'AAPL', 'MSFT') for which you want 98 | to retrieve the current quote data. 99 | last_price (bool, optional): If True, only fetch the last price of the symbol. Default is False. 100 | 101 | Returns: 102 | pandas.DataFrame or float: A DataFrame containing the current quote data for the specified symbol 103 | or just the last price as a float if last_price is set to True. 104 | 105 | Example: 106 | # Retrieve current quote data for symbol 'CCL' and transpose the DataFrame for easy viewing 107 | quote_data = q.get_quote_day(symbol='CCL').T 108 | 109 | Sample Output: 110 | 0 111 | symbol CCL 112 | description Carnival Corp 113 | exch N 114 | type stock 115 | last 15.73 116 | change -0.09 117 | volume 16767253 118 | open 15.83 119 | high 16.06 120 | low 15.58 121 | close 15.73 122 | bid 15.7 123 | ask 15.73 124 | change_percentage -0.57 125 | average_volume 39539044 126 | last_volume 0 127 | trade_date 1693609200001 128 | prevclose 15.82 129 | week_52_high 19.55 130 | week_52_low 6.11 131 | bidsize 11 132 | bidexch P 133 | bid_date 1693612800000 134 | asksize 29 135 | askexch P 136 | ask_date 1693612764000 137 | root_symbols CCL 138 | 139 | # Retrieve only the last price for symbol 'CCL' 140 | last_price = q.get_quote_day(symbol='CCL', last_price=True) 141 | Sample Output: 15.73 142 | ''' 143 | 144 | r = requests.get( 145 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_ENDPOINT), 146 | params = {'symbols':symbol, 'greeks':'false'}, 147 | headers = self.REQUESTS_HEADERS 148 | ); 149 | 150 | df_quote = pd.json_normalize(r.json()['quotes']['quote']); 151 | 152 | if last_price: 153 | return float(df_quote['last']); 154 | 155 | return df_quote; 156 | 157 | def get_timesales (self, symbol, interval='1min', start_time=False, end_time=False): 158 | ''' 159 | This function returns the tick data for `symbol` in increments specified by `interval`. 160 | Eventually, we can use this to analyze/visualze stock data in a time series context. 161 | 162 | Arguments `start_time` and `end_time` must be strings with format 'YYYY-MM-DD HH:MM' 163 | 164 | Sample output: 165 | >>> quotes.get_timesales('VZ', start_time='2023-09-27 09:45', end_time='2023-09-27 14:00') 166 | time timestamp price open high low close volume vwap 167 | 0 2023-09-27T09:45:00 1695822300 32.92995 32.9500 32.9500 32.9099 32.915 39077 32.924828 168 | 1 2023-09-27T09:46:00 1695822360 32.89560 32.9112 32.9112 32.8800 32.895 32867 32.891113 169 | 2 2023-09-27T09:47:00 1695822420 32.88750 32.8950 32.9100 32.8650 32.910 75720 32.888736 170 | 3 2023-09-27T09:48:00 1695822480 32.91750 32.9100 32.9250 32.9100 32.910 15126 32.913109 171 | 4 2023-09-27T09:49:00 1695822540 32.91000 32.9100 32.9200 32.9000 32.920 20335 32.907385 172 | .. ... ... ... ... ... ... ... ... ... 173 | 251 2023-09-27T13:56:00 1695837360 32.35425 32.3450 32.3655 32.3430 32.360 58256 32.354522 174 | 252 2023-09-27T13:57:00 1695837420 32.35500 32.3500 32.3600 32.3500 32.360 15825 32.355307 175 | 253 2023-09-27T13:58:00 1695837480 32.36125 32.3599 32.3700 32.3525 32.365 34624 32.362786 176 | 254 2023-09-27T13:59:00 1695837540 32.37500 32.3700 32.3900 32.3600 32.390 27728 32.370699 177 | 255 2023-09-27T14:00:00 1695837600 32.38750 32.3866 32.3950 32.3800 32.385 53837 32.386281 178 | 179 | (vwap = volume weighted average price during the interval) 180 | ''' 181 | r_params = {'symbol':symbol}; 182 | if start_time: 183 | r_params['start'] = start_time; 184 | if end_time: 185 | r_params['end'] = end_time; 186 | 187 | r = requests.get( 188 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_TIMESALES_ENDPOINT), 189 | params = r_params, 190 | headers = self.REQUESTS_HEADERS 191 | ); 192 | 193 | return pd.json_normalize(r.json()['series']['data']); 194 | 195 | 196 | def search_companies (self, query): 197 | if not query: 198 | return "Need that search term yo"; 199 | 200 | r = requests.get( 201 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_SEARCH_ENDPOINT), 202 | params = {'q': query, 'indexes':'false'}, 203 | headers = self.REQUESTS_HEADERS 204 | ); 205 | 206 | if not r.json()['securities']: 207 | return "Nothing found"; 208 | 209 | return pd.DataFrame(r.json()['securities']['security']); -------------------------------------------------------------------------------- /Legacy/uvatradier/dist/uvatradier-0.0.2-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thammo4/uvatradier/e3b97fd70e2c3e8f1bb35b6e357721cc022cae24/Legacy/uvatradier/dist/uvatradier-0.0.2-py3-none-any.whl -------------------------------------------------------------------------------- /Legacy/uvatradier/dist/uvatradier-0.0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thammo4/uvatradier/e3b97fd70e2c3e8f1bb35b6e357721cc022cae24/Legacy/uvatradier/dist/uvatradier-0.0.2.tar.gz -------------------------------------------------------------------------------- /Legacy/uvatradier/dist/uvatradier-0.0.3-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thammo4/uvatradier/e3b97fd70e2c3e8f1bb35b6e357721cc022cae24/Legacy/uvatradier/dist/uvatradier-0.0.3-py3-none-any.whl -------------------------------------------------------------------------------- /Legacy/uvatradier/dist/uvatradier-0.0.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thammo4/uvatradier/e3b97fd70e2c3e8f1bb35b6e357721cc022cae24/Legacy/uvatradier/dist/uvatradier-0.0.3.tar.gz -------------------------------------------------------------------------------- /Legacy/uvatradier/dist/uvatradier-0.0.4-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thammo4/uvatradier/e3b97fd70e2c3e8f1bb35b6e357721cc022cae24/Legacy/uvatradier/dist/uvatradier-0.0.4-py3-none-any.whl -------------------------------------------------------------------------------- /Legacy/uvatradier/dist/uvatradier-0.0.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thammo4/uvatradier/e3b97fd70e2c3e8f1bb35b6e357721cc022cae24/Legacy/uvatradier/dist/uvatradier-0.0.4.tar.gz -------------------------------------------------------------------------------- /Legacy/uvatradier/examples/account_ex.py: -------------------------------------------------------------------------------- 1 | # 2 | # Example - Using uvatradier to fetch account balance information 3 | # 4 | 5 | import os, dotenv; 6 | from uvatradier import Tradier, Account; 7 | 8 | # Create .env file within current working directory. 9 | # Add the following to your .env file. Specify the correct account number and authorization token for your account. 10 | # tradier_acct = 11 | # tradier_token = 12 | 13 | dotenv.load_dotenv(); 14 | 15 | ACCOUNT_NUMBER = os.getenv('tradier_acct'); AUTH_TOKEN = os.getenv('tradier_token'); 16 | 17 | # Initialize a new Account object 18 | account = Account(ACCOUNT_NUMBER, AUTH_TOKEN); 19 | 20 | # Fetch account balance info 21 | balance = account.get_account_balance(); 22 | 23 | print(balance); 24 | -------------------------------------------------------------------------------- /Legacy/uvatradier/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | with open('README.md', 'r', encoding='utf-8') as fh: 5 | long_description = fh.read(); 6 | 7 | setup( 8 | name='uvatradier', 9 | version='0.0.4', 10 | author='tom hammons', 11 | description='wahoowah', 12 | long_description=long_description, 13 | long_description_content_type='text/markdown', 14 | url='https://github.com/thammo4/uvatradier', 15 | packages=find_packages(), 16 | project_urls={'Bug Tracker':'https://github.com/thammo4/uvatradier/issues'} 17 | ); -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: uvatradier 3 | Version: 0.0.4 4 | Summary: wahoowah 5 | Home-page: https://github.com/thammo4/uvatradier 6 | Author: tom hammons 7 | Project-URL: Bug Tracker, https://github.com/thammo4/uvatradier/issues 8 | Description-Content-Type: text/markdown 9 | License-File: LICENSE 10 | 11 | # tradier 12 | 13 | Python wrapper for the tradier brokerage API 14 | 15 | Class based functionality currently in development. Code is found in tradier.py 16 | 17 | 18 | To get tradier.py to work, need to have 'tradier_acct' and 'tradier_token' defined in a .env file in the same directory. 19 | 20 | From a python file / jupyter notebook, just run `from tradier import *` 21 | -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | LICENSE 2 | README.md 3 | setup.py 4 | uvatradier/__init__.py 5 | uvatradier/account.py 6 | uvatradier/base.py 7 | uvatradier/equity_order.py 8 | uvatradier/options_data.py 9 | uvatradier/options_order.py 10 | uvatradier/quotes.py 11 | uvatradier/tradier.py 12 | uvatradier.egg-info/PKG-INFO 13 | uvatradier.egg-info/SOURCES.txt 14 | uvatradier.egg-info/dependency_links.txt 15 | uvatradier.egg-info/top_level.txt -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | uvatradier 2 | -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | from .account import Account 3 | from .quotes import Quotes 4 | from .equity_order import EquityOrder 5 | from .options_data import OptionsData 6 | from .options_order import OptionsOrder 7 | 8 | print('hello, world!') -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/account.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import pandas as pd 3 | 4 | from .base import Tradier 5 | 6 | class Account (Tradier): 7 | def __init__ (self, account_number, auth_token): 8 | Tradier.__init__(self, account_number, auth_token); 9 | 10 | # 11 | # Account endpoints 12 | # 13 | 14 | self.PROFILE_ENDPOINT = "v1/user/profile"; # GET 15 | 16 | self.POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(account_number); # GET 17 | 18 | 19 | self.ACCOUNT_BALANCE_ENDPOINT = "v1/accounts/{}/balances".format(account_number); # GET 20 | self.ACCOUNT_GAINLOSS_ENDPOINT = "v1/accounts/{}/gainloss".format(account_number); # GET 21 | self.ACCOUNT_HISTORY_ENDPOINT = "v1/accounts/{}/history".format(account_number); # GET 22 | self.ACCOUNT_POSITIONS_ENDPOINT = "v1/accounts/{}/positions".format(account_number); # GET 23 | 24 | self.ACCOUNT_INDIVIDUAL_ORDER_ENDPOINT = "v1/accounts/{account_id}/orders/{order_id}"; # GET 25 | 26 | 27 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(account_number); # GET 28 | 29 | def get_user_profile(self): 30 | ''' 31 | Fetch the user profile information from the Tradier Account API. 32 | 33 | This function makes a GET request to the Tradier Account API to retrieve the user profile 34 | information associated with the trading account linked to the provided credentials. 35 | The API response is expected to be in JSON format, containing details about the user profile. 36 | 37 | Returns: 38 | pandas.DataFrame: A DataFrame containing user profile information. 39 | 40 | Example: 41 | # Retrieve user profile information 42 | user_profile = get_user_profile() 43 | transposed_profile = user_profile.T # Transpose the DataFrame for easy viewing. 44 | 45 | Example DataFrame (transposed): 46 | 47 | id id-sb-2r01lpprbg 48 | name Fat Albert 49 | account.account_number ABC1234567 50 | account.classification individual 51 | account.date_created 2021-06-23T22:04:20.000Z 52 | account.day_trader False 53 | account.option_level 6 54 | account.status active 55 | account.type margin 56 | account.last_update_date 2021-06-23T22:04:20.000Z 57 | ''' 58 | r = requests.get( 59 | url = '{}/{}'.format(self.SANDBOX_URL, self.PROFILE_ENDPOINT), 60 | params = {}, 61 | headers = self.REQUESTS_HEADERS 62 | ); 63 | 64 | return pd.json_normalize(r.json()['profile']); 65 | 66 | 67 | def get_account_balance(self): 68 | ''' 69 | Fetch the account balance information from the Tradier Account API. 70 | 71 | This function makes a GET request to the Tradier Account API to retrieve the account 72 | balance information for the trading account associated with the provided credentials. 73 | The API response is expected to be in JSON format, containing details about the account 74 | balance. 75 | 76 | Returns: 77 | pandas.DataFrame: A DataFrame containing account balance information. 78 | 79 | Example: 80 | # Retrieve account balance information 81 | account_balance = get_account_balance() 82 | transposed_balance = account_balance.T # Transpose the DataFrame for easy viewing. 83 | 84 | Example DataFrame (transposed): 85 | 0 86 | option_short_value -147.0 87 | total_equity 74314.82 88 | account_number ABC1234567 89 | account_type margin 90 | close_pl 0 91 | current_requirement 17595.08 92 | equity 0 93 | long_market_value 34244.16 94 | market_value 34097.16 95 | open_pl -225300.265 96 | option_long_value 1054.0 97 | option_requirement 1000.0 98 | pending_orders_count 8 99 | short_market_value -147.0 100 | stock_long_value 33190.16 101 | total_cash 40217.66 102 | uncleared_funds 0 103 | pending_cash 16892.9 104 | margin.fed_call 0 105 | margin.maintenance_call 0 106 | margin.option_buying_power 38919.84 107 | margin.stock_buying_power 77839.68 108 | margin.stock_short_value 0 109 | margin.sweep 0 110 | ''' 111 | r = requests.get( 112 | url = '{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_BALANCE_ENDPOINT), 113 | params = {}, 114 | headers = self.REQUESTS_HEADERS 115 | ); 116 | 117 | return pd.json_normalize(r.json()['balances']); 118 | 119 | 120 | def get_gainloss (self): 121 | ''' 122 | Get cost basis information for a specific user account. 123 | This includes information for all closed positions. 124 | Cost basis information is updated through a nightly batch reconciliation process with tradier clearing firm. 125 | 126 | Returns: 127 | Pandas dataframe with columns [close_date, cost, gain_loss, gain_loss_percent, open_date, proceeds, quantity, symbol, term] 128 | 129 | Example: 130 | >>> account.get_gainloss().head() 131 | close_date cost gain_loss gain_loss_percent open_date proceeds quantity symbol term 132 | 0 2023-09-13T00:00:00.000Z 194700.0 -30600.0 -15.72 2023-08-25T00:00:00.000Z 164100.0 10.0 LMT240119C00260000 19 133 | 1 2023-09-13T00:00:00.000Z 10212.2 -432.6 -4.24 2023-09-06T00:00:00.000Z 9779.6 20.0 KLAC 7 134 | 2 2023-09-13T00:00:00.000Z 2300.0 175.0 7.61 2023-08-24T00:00:00.000Z 2475.0 1.0 HAL251219C00018000 20 135 | 3 2023-09-13T00:00:00.000Z 20700.0 1620.0 7.83 2023-08-24T00:00:00.000Z 22320.0 9.0 HAL251219C00018000 20 136 | 4 2023-09-06T00:00:00.000Z 16967.0 -193.0 -1.14 2023-09-01T00:00:00.000Z 16774.0 100.0 TXN 5 137 | ''' 138 | r = requests.get( 139 | url = '{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_GAINLOSS_ENDPOINT), 140 | params = {}, 141 | headers = self.REQUESTS_HEADERS 142 | ); 143 | 144 | return pd.json_normalize(r.json()['gainloss']['closed_position']); 145 | 146 | 147 | def get_orders (self): 148 | ''' 149 | This function returns a pandas DataFrame. 150 | Each row denotes a queued order. Each column contiains a feature_variable pertaining to the order. 151 | Transposed sample output has the following structure: 152 | 153 | >>> account.get_orders().T 154 | 0 1 155 | id 8248093 8255194 156 | type stop_limit market 157 | symbol UNP CF 158 | side buy buy 159 | quantity 3.0 10.0 160 | status open filled 161 | duration day gtc 162 | price 200.0 NaN 163 | avg_fill_price 0.0 87.39 164 | exec_quantity 0.0 10.0 165 | last_fill_price 0.0 87.39 166 | last_fill_quantity 0.0 10.0 167 | remaining_quantity 3.0 0.0 168 | stop_price 200.0 NaN 169 | create_date 2023-09-25T20:29:10.351Z 2023-09-26T14:45:00.155Z 170 | transaction_date 2023-09-26T12:30:19.152Z 2023-09-26T14:45:00.216Z 171 | class equity equity 172 | ''' 173 | 174 | r = requests.get( 175 | url='{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 176 | params={'includeTags':'true'}, 177 | headers=self.REQUESTS_HEADERS 178 | ); 179 | 180 | # return pd.DataFrame(r.json()['orders']); 181 | return pd.json_normalize(r.json()['orders']['order']); 182 | 183 | 184 | def get_positions(self, symbols=False, equities=False, options=False): 185 | ''' 186 | Fetch and filter position data from the Tradier Account API. 187 | 188 | This function makes a GET request to the Tradier Account API to retrieve position 189 | information related to a trading account. The API response is expected to be in 190 | JSON format, containing details about the positions held in the account. 191 | 192 | Args: 193 | symbols (list, optional): A list of trading symbols (e.g., stock ticker symbols) 194 | to filter the position data. If provided, only positions 195 | matching these symbols will be included. 196 | equities (bool, optional): If True, filter the positions to include only equities 197 | (stocks) with symbols less than 5 characters in length. 198 | If False, no filtering based on equities will be applied. 199 | options (bool, optional): If True, filter the positions to include only options 200 | with symbols exceeding 5 characters in length. 201 | If False, no filtering based on options will be applied. 202 | 203 | Returns: 204 | pandas.DataFrame: A DataFrame containing filtered position information based on 205 | the specified criteria. 206 | 207 | Example: 208 | # Retrieve all positions without filtering 209 | all_positions = get_positions() 210 | 211 | # Retrieve positions for specific symbols ('AAPL', 'GOOGL') 212 | specific_positions = get_positions(symbols=['AAPL', 'GOOGL']) 213 | 214 | # Retrieve only equities 215 | equities_positions = get_positions(equities=True) 216 | 217 | # Retrieve only options 218 | options_positions = get_positions(options=True) 219 | ''' 220 | r = requests.get(url='{}/{}'.format(self.SANDBOX_URL, self.ACCOUNT_POSITIONS_ENDPOINT), params={}, headers=self.REQUESTS_HEADERS); 221 | if r.json(): 222 | positions_df = pd.DataFrame(r.json()['positions']['position']); 223 | if symbols: 224 | positions_df = positions_df.query('symbol in @symbols'); 225 | if equities: 226 | positions_df = positions_df[positions_df['symbol'].str.len() < 5]; 227 | options = False; 228 | if options: 229 | positions_df = positions_df[positions_df['symbol'].str.len() > 5]; 230 | return positions_df; 231 | -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/base.py: -------------------------------------------------------------------------------- 1 | class Tradier: 2 | def __init__ (self, account_number, auth_token): 3 | 4 | # 5 | # Define account credentials 6 | # 7 | 8 | self.ACCOUNT_NUMBER = account_number; 9 | self.AUTH_TOKEN = auth_token; 10 | self.REQUESTS_HEADERS = {'Authorization':'Bearer {}'.format(self.AUTH_TOKEN), 'Accept':'application/json'} 11 | 12 | 13 | # 14 | # Define base url for paper trading and individual API endpoints 15 | # 16 | 17 | self.SANDBOX_URL = 'https://sandbox.tradier.com'; -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/equity_order.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | 4 | class EquityOrder (Tradier): 5 | def __init__ (self, account_number, auth_token): 6 | Tradier.__init__(self, account_number, auth_token); 7 | 8 | # 9 | # Order endpoint 10 | # 11 | 12 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(self.ACCOUNT_NUMBER); # POST 13 | 14 | def order (self, symbol, side, quantity, order_type, duration='day', limit_price=False, stop_price=False): 15 | ''' 16 | Example of how to run: 17 | >>> eo = EquityOrder(ACCOUNT_NUMBER, AUTH_TOKEN) 18 | >>> eo.order(symbol='QQQ', side='buy', quantity=10, order_type='market', duration='gtc'); 19 | {'order': {'id': 8256590, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 20 | ''' 21 | 22 | # 23 | # Define initial requests parameters dictionary whose fields are applicable to all order_type values 24 | # 25 | 26 | r_params = { 27 | 'class' : 'equity', 28 | 'symbol' : symbol, 29 | 'side' : side, 30 | 'quantity' : quantity, 31 | 'type' : order_type, 32 | 'duration' : duration 33 | }; 34 | 35 | # 36 | # If the order_type is limit, stop, or stop_limit --> Set the appropriate limit price or stop price 37 | # 38 | 39 | if order_type.lower() in ['limit', 'stop_limit']: 40 | r_params['price'] = limit_price; 41 | if order_type.lower() in ['stop', 'stop_limit']: 42 | r_params['stop'] = stop_price; 43 | 44 | r = requests.post( 45 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 46 | params = r_params, 47 | headers=self.REQUESTS_HEADERS 48 | ); 49 | 50 | return r.json(); -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/options_data.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | class OptionsData (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Option data endpoints 9 | # 10 | 11 | self. OPTIONS_STRIKE_ENDPOINT = "v1/markets/options/strikes"; # GET 12 | self. OPTIONS_CHAIN_ENDPOINT = "v1/markets/options/chains"; # GET 13 | self. OPTIONS_EXPIRY_ENDPOINT = "v1/markets/options/expirations"; # GET 14 | self. OPTIONS_SYMBOL_ENDPOINT = "v1/markets/options/lookup"; # GET 15 | 16 | 17 | # 18 | # Fetch all option chain data for a single day of contract expirations 19 | # 20 | 21 | def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False): 22 | ''' 23 | This function returns option chain data for a given symbol. 24 | All contract expirations occur on the same expiry date 25 | ''' 26 | 27 | # 28 | # Set the contract expiration date to the nearest valid date 29 | # 30 | 31 | if not expiry: 32 | expiry = self.get_expiry_dates(symbol)[0]; 33 | 34 | # 35 | # Define request object for given symbol and expiration 36 | # 37 | 38 | r = requests.get( 39 | url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_CHAIN_ENDPOINT), 40 | params = {'symbol':symbol, 'expiration':expiry, 'greeks':'false'}, 41 | headers = self.REQUESTS_HEADERS 42 | ); 43 | 44 | # 45 | # Convert returned json -> pandas dataframe 46 | # 47 | 48 | option_df = pd.DataFrame(r.json()['options']['option']); 49 | 50 | 51 | # 52 | # Remove columns which have the same value for every row 53 | # 54 | 55 | cols_to_drop = option_df.nunique()[option_df.nunique() == 1].index; 56 | option_df = option_df.drop(cols_to_drop, axis=1); 57 | 58 | # 59 | # Remove columns which have NaN in every row 60 | # 61 | 62 | cols_to_drop = option_df.nunique()[option_df.nunique() == 0].index; 63 | option_df = option_df.drop(cols_to_drop, axis=1); 64 | 65 | 66 | # 67 | # Remove description column because its information is redundant 68 | # 69 | 70 | cols_to_drop = ['description']; 71 | option_df = option_df.drop(cols_to_drop, axis=1); 72 | 73 | 74 | # 75 | # Filter rows per strike_low and strike_high 76 | # 77 | 78 | if strike_low: 79 | option_df = option_df.query('strike >= @strike_low'); 80 | 81 | if strike_high: 82 | option_df = option_df.query('strike <= @strike_high'); 83 | 84 | if strike: 85 | option_df = option_df.query('strike == @strike'); 86 | 87 | if option_type in ['call', 'put']: 88 | option_df = option_df.query('option_type == @option_type'); 89 | 90 | 91 | if option_type: 92 | if option_type in ['call', 'put']: 93 | option_df = option_df.query('option_type == @option_type'); 94 | 95 | # 96 | # Return the resulting dataframe whose rows are individual contracts with expiration `expiry` 97 | # 98 | 99 | return option_df; 100 | 101 | 102 | def get_expiry_dates (self, symbol, strikes=False): 103 | ''' 104 | Get the expiry dates for options on a given symbol. 105 | 106 | Args: 107 | symbol (str): The symbol for which to retrieve expiry dates. 108 | strikes (bool, optional): Whether to include strike prices for each expiry date. Defaults to False. 109 | 110 | Returns: 111 | If strikes=False -> returns list or list of dict: A list of expiry dates in the format 'YYYY-MM-DD'. 112 | If strikes=True -> returns a list of dictionaries with expiry date and associated strike prices. 113 | 114 | Example: 115 | >>> options = OptionsData(ACCOUNT_NUMBER, AUTH_TOKEN) 116 | >>> options.get_expiry_dates(symbol='DFS') 117 | ['2023-09-08', '2023-09-15', '2023-09-22', '2023-09-29', '2023-10-06', '2023-10-13', '2023-10-20', '2023-11-17', '2024-01-19', '2024-04-19', '2024-06-21', '2025-01-17'] 118 | 119 | >>> options.get_expiry_dates(symbol='DFS', strikes=True) 120 | [{'date': '2023-09-08', 'strikes': {'strike': [59.0, 60.0, 61.0, ...]}, {'date': '2023-09-15', 'strikes': {'strike': [55.0, 60.0, ...}}, ...] 121 | ''' 122 | 123 | r = requests.get( 124 | url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_EXPIRY_ENDPOINT), 125 | params = {'symbol':symbol, 'includeAllRoots':True, 'strikes':str(strikes)}, 126 | headers = self.REQUESTS_HEADERS 127 | ); 128 | 129 | if r.status_code != 200: 130 | return 'wtf'; 131 | 132 | expiry_dict = r.json()['expirations']; 133 | 134 | # Without strikes, we can get a list of dates 135 | if strikes == False: 136 | return expiry_dict['date']; 137 | 138 | # Otherwise, return a list whose elements are dicts with format {'date':[list_of_strikes]} 139 | return expiry_dict['expiration']; 140 | 141 | 142 | # 143 | # This function returns a list of symbols. Each element has a format 144 | # COP240216C00115000 145 | # 146 | # i.e. 147 | # 148 | # COP 240216 C 00115000 149 | # 150 | 151 | # ########################## 152 | # TODO (9/27): 153 | # 1. Change get_options_symbols so that it returns the dataframe format by default 154 | # 2. Investigate the KeyError: 'expiration_date' error produced from `options.get_options_symbols('BWA', df=True)` 155 | # • Issue arises because there are two different symbols for BorgWarner Inc. {'BWA', 'BWA1'} 156 | # • Not sure how to fix this problem yet though 157 | # ########################## 158 | 159 | def get_options_symbols (self, symbol, df=False): 160 | ''' 161 | This function provides a convenient wrapper to fetch option symbols 162 | for a specified symbol 163 | 164 | If df=False, then a list of OCC options symbols is returned. 165 | For an n-element list, if i=0,...,(n-1), then every (option_list[i-1], option_list[i]) pair of elements represents a pair of call/put options. 166 | 167 | If df=True, then a pandas.DataFrame object is returned. 168 | Each row of the dataframe represents a single put or call contract. 169 | The first column is the OCC symbol. The subsequent columns are the parsed values of the OCC symbol. 170 | ''' 171 | 172 | # 173 | # Helper function to convert the get_option_symbols list into a dataframe 174 | # 175 | 176 | def symbol_list_to_df (option_list): 177 | ''' 178 | This is a helper function called from get_option_symbols. 179 | It will parse a list of option symbols into their constituent components 180 | For example: 181 | option_symbol 182 | = [underlying_symbol, expiry_date, option_type, option_id] 183 | = [LMT, 240119, C, 00300000] 184 | ''' 185 | parsed_options = [] 186 | 187 | for option in option_list: 188 | match = re.match(r'([A-Z]+)(\d{6})([CP])(\d+)', option) 189 | if match: 190 | root_symbol, expiration_date, option_type, strike_price = match.groups() 191 | parsed_options.append({'symbol':option,'root_symbol':root_symbol, 'expiration_date':expiration_date, 'option_type':option_type, 'strike_price':strike_price}) 192 | return pd.DataFrame(parsed_options); 193 | 194 | 195 | # 196 | # Helper function to turn the option dates into standard date format 197 | # 198 | def parse_option_expiries (expiry_list): 199 | ''' 200 | Helper function to turn the option dates into standard date format 201 | ''' 202 | 203 | formatted_expiries = []; 204 | for x in expiry_list: 205 | formatted_expiries.append(datetime.strptime(x, '%y%m%d').strftime('%Y-%m-%d')); 206 | 207 | return formatted_expiries; 208 | 209 | 210 | r = requests.get( 211 | url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_SYMBOL_ENDPOINT), 212 | params = {'underlying':symbol}, 213 | headers = self.REQUESTS_HEADERS 214 | ); 215 | 216 | option_list = r.json()['symbols'][0]['options']; 217 | 218 | if df: 219 | option_df = symbol_list_to_df(option_list); 220 | option_df['expiration_date'] = parse_option_expiries(option_df['expiration_date']); 221 | return option_df; 222 | 223 | return option_list; -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/options_order.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | class OptionsOrder (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Order endpoint 9 | # 10 | 11 | self.ORDER_ENDPOINT = "v1/accounts/{}/orders".format(ACCOUNT_NUMBER); # POST 12 | 13 | 14 | # 15 | # Bear-put spread 16 | # 17 | 18 | def bear_put_spread (self, underlying, option0, quantity0, option1, quantity1, duration='day'): 19 | ''' 20 | Parameters 21 | underlying: asset symbol (e.g. 'SPY') 22 | option0: OCC symbol of 23 | ''' 24 | r = requests.post( 25 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 26 | data = { 27 | 'class' : 'multileg', 28 | 'symbol' : underlying, 29 | 'type' : 'market', 30 | 'duration' : duration, 31 | 'option_symbol[0]' : option0, 32 | 'side[0]' : 'buy_to_open', 33 | 'quantity[0]' : quantity0, 34 | 'option_symbol[1]' : option1, 35 | 'side[1]' : 'sell_to_open', 36 | 'quantity[1]' : quantity1 37 | }, 38 | headers = self.REQUESTS_HEADERS 39 | ); 40 | 41 | return r.json(); 42 | 43 | 44 | # 45 | # Bear-call spread 46 | # 47 | 48 | def bear_call_spread (self, underlying, option0, quantity0, option1, quantity1, duration='day'): 49 | ''' 50 | Bear call spread example: 51 | • XYZ @ $50/share 52 | • Pr(XYZ < $55/share) > .50 53 | • Legs 54 | • Short Call with K1 ≥ S (e.g. K1=55 > S=50) and receive $3 premium 55 | • Long Call with K2 > K1 ≥ S (e.g. K2=60 > K1=55 ≥ S=50) and pay $1 premium 56 | • Expiry t=T 57 | • If S(T) < K1 -> payoff = premium differential 58 | • If K1 < S(T) < K2 59 | • short call exercised and must sell at K1 = $55 60 | • long call expires OTM 61 | • payoff = (K1-K2) + (premium differential) < 0 62 | • If S(T) > K2 > K1 63 | • short call exercised and must sell at K1 = $55 64 | • long call exercised and can buy XYZ at K2 = $60 65 | • payoff = (K1-K2) + (premium differential) < 0 66 | ''' 67 | r = requests.post( 68 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 69 | data = { 70 | 'class' : 'multileg', 71 | 'symbol' : underlying, 72 | 'type' : 'market', 73 | 'duration' : duration, 74 | 'option_symbol[0]' : option0, 75 | 'side[0]' : 'buy_to_open', 76 | 'quantity[0]' : quantity0, 77 | 'option_symbol[1]' : option1, 78 | 'side[1]' : 'sell_to_open', 79 | 'quantity[1]' : quantity1 80 | }, 81 | headers = self.REQUESTS_HEADERS 82 | ); 83 | 84 | return r.json(); 85 | 86 | 87 | 88 | # 89 | # Bull-put spread 90 | # 91 | 92 | def bull_put_spread (self, underlying_symbol, option_symbol_0, quantity_0, option_symbol_1, quantity_1, duration='day'): 93 | r = requests.post( 94 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 95 | data = { 96 | 'class' : 'multileg', 97 | 'symbol' : underlying_symbol, 98 | 'type' : 'market', 99 | 'duration' : duration, 100 | 'option_symbol[0]' : option_symbol_0, 101 | 'side[0]' : 'sell_to_open', 102 | 'quantity[0]' : quantity_0, 103 | 'option_symbol[1]' : option_symbol_1, 104 | 'side[1]' : 'buy_to_open', 105 | 'quantity[1]' : quantity_1 106 | }, 107 | headers = self.REQUESTS_HEADERS 108 | ); 109 | 110 | return r.json(); 111 | 112 | # 113 | # Bull-call spread 114 | # 115 | 116 | def bull_call_spread (self, underlying_symbol, option_symbol_0, quantity_0, option_symbol_1, quantity_1, duration='day'): 117 | r = requests.post( 118 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 119 | data = { 120 | 'class' : 'multileg', 121 | 'symbol' : underlying_symbol, 122 | 'type' : 'market', 123 | 'duration' : duration, 124 | 'option_symbol[0]' : option_symbol_0, 125 | 'side[0]' : 'buy_to_open', 126 | 'quantity[0]' : quantity_0, 127 | 'option_symbol[1]' : option_symbol_1, 128 | 'side[1]' : 'sell_to_open', 129 | 'quantity[1]' : quantity_1 130 | }, 131 | headers = self.REQUESTS_HEADERS 132 | ); 133 | 134 | return r.json(); 135 | 136 | 137 | # def extract_stock_symbol (self, occ_symbol): 138 | def extract_occ_underlying (self, occ_symbol): 139 | match = re.match(r'^([A-Z]){1,4}\d', occ_symbol); 140 | if match: 141 | return match.group(1); 142 | else: 143 | return None; 144 | 145 | 146 | def options_order (self, occ_symbol, order_type, side, quantity, underlying=False, limit_price=False, stop_price=False, duration='day'): 147 | ''' 148 | Params: 149 | • occ_symbol = options contract (e.g. 'TER230915C00110000') 150 | • order_type = The type of order to be placed. One of: market, limit, stop, stop_limit 151 | • side = The side of the order. One of: buy_to_open, buy_to_close, sell_to_open, sell_to_close 152 | • quantity = Number of contracts to buy/sell 153 | • underlying = Underlying symbol. If not supplied, will be inferred from occ_symbol. (e.g. 'TER') 154 | • duration = Time the order will remain active. One of: day, gtc, pre, post 155 | • limit_price = Limit Price for limit or stop_limit orders 156 | • stop_price = Stop Price for stop or stop_limit orders 157 | 158 | Returns: 159 | • json from the requests.post call to the order endpoint 160 | 161 | Notes: 162 | • If order_type='limit' or order_type = 'stop_limit', then must specify limit_price 163 | • If order_type='stop' or order_type = 'stop_limit', then must specify stop_price 164 | 165 | Example: 166 | >>> options_order.options_order(occ_symbol='LMT240119C00260000', order_type='market', side='sell_to_close', quantity=10.0) 167 | # Returns: {'order': {'id': 8042606, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 168 | ''' 169 | if not underlying: 170 | underlying = self.extract_occ_underlying(occ_symbol); 171 | 172 | r_data = { 173 | 'class' : 'option', 174 | 'symbol' : underlying, 175 | 'option_symbol' : occ_symbol, 176 | 'side' : side, 177 | 'quantity' : quantity, 178 | 'type' : order_type, 179 | 'duration' : duration 180 | }; 181 | 182 | if order_type in ['limit', 'stop_limit']: 183 | if not limit_price: 184 | print('Need limit price.'); 185 | return None; 186 | r_data['price'] = limit_price; 187 | if order_type in ['stop', 'stop_limit']: 188 | if not stop_price: 189 | print('Need stop price.'); 190 | return None; 191 | r_data['stop'] = stop_price; 192 | 193 | r = requests.post( 194 | url = '{}/{}'.format(self.SANDBOX_URL, self.ORDER_ENDPOINT), 195 | data = r_data, 196 | headers = self.REQUESTS_HEADERS 197 | ); 198 | 199 | return r.json(); -------------------------------------------------------------------------------- /Legacy/uvatradier/uvatradier/quotes.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | class Quotes (Tradier): 4 | def __init__ (self, account_number, auth_token): 5 | Tradier.__init__(self, account_number, auth_token); 6 | 7 | # 8 | # Quotes endpoints for market data about equities 9 | # 10 | 11 | self.QUOTES_ENDPOINT = "v1/markets/quotes"; # GET (POST) 12 | self.QUOTES_HISTORICAL_ENDPOINT = "v1/markets/history"; # GET 13 | self.QUOTES_SEARCH_ENDPOINT = "v1/markets/search"; # GET 14 | self.QUOTES_TIMESALES_ENDPOINT = "v1/markets/timesales"; # GET 15 | 16 | def get_historical_quotes (self, symbol, interval='daily', start_date=False, end_date=False): 17 | 18 | ''' 19 | Fetch historical stock data for a given symbol from the Tradier Account API. 20 | 21 | This function makes a GET request to the Tradier Account API to retrieve historical stock 22 | data for a specified symbol within a specified time interval. 23 | 24 | Args: 25 | symbol (str): The trading symbol of the stock (e.g., 'AAPL', 'MSFT') for which you want 26 | to retrieve historical data. 27 | interval (str, optional): The time interval for historical data. Default is 'daily'. Alt values are 'weekly' or 'monthly'. 28 | start_date (str, optional): The start date for historical data in the format 'YYYY-MM-DD'. 29 | If not provided, the function will default to the most recent Monday. 30 | end_date (str, optional): The end date for historical data in the format 'YYYY-MM-DD'. 31 | If not provided, the function will default to the current date. 32 | 33 | Returns: 34 | pandas.DataFrame: A DataFrame containing historical stock data for the specified symbol. 35 | 36 | Example: 37 | # Create a Quotes instance 38 | q = Quotes(ACCOUNT_NUMBER, AUTH_TOKEN) 39 | 40 | # Retrieve historical stock data for symbol 'BIIB' 41 | historical_data = q.get_historical_quotes(symbol='BIIB') 42 | 43 | Sample Output: 44 | date open high low close volume 45 | 0 2023-08-28 265.40 266.470 263.54 265.05 359872 46 | 1 2023-08-29 265.35 268.150 265.11 268.00 524972 47 | 2 2023-08-30 268.84 269.460 265.25 267.18 552728 48 | 3 2023-08-31 266.83 269.175 265.32 267.36 1012842 49 | 4 2023-09-01 269.01 269.720 266.91 267.17 522401 50 | ''' 51 | 52 | # 53 | # Helper function used to index the start of the trading week 54 | # 55 | 56 | def last_monday (input_date): 57 | ''' 58 | Find the date of the previous Monday for a given input date. 59 | 60 | Args: 61 | input_date (datetime.date): the input date 62 | 63 | Returns: 64 | datetime.date: The date of the previous Monday. 65 | ''' 66 | 67 | return (input_date - timedelta(days=(input_date.weekday()))); 68 | 69 | if not end_date: 70 | end_date = datetime.today().strftime('%Y-%m-%d'); 71 | 72 | if not start_date: 73 | tmp = datetime.strptime(end_date, '%Y-%m-%d'); 74 | start_date = last_monday(tmp).strftime('%Y-%m-%d'); 75 | 76 | r = requests.get( 77 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_HISTORICAL_ENDPOINT), 78 | params = { 79 | 'symbol' : symbol, 80 | 'interval' : interval, 81 | 'start' : start_date, 82 | 'end' : end_date 83 | }, 84 | headers = self.REQUESTS_HEADERS 85 | ); 86 | 87 | return pd.DataFrame(r.json()['history']['day']); 88 | 89 | def get_quote_day (self, symbol, last_price=False): 90 | ''' 91 | Fetch the current quote data for a given symbol from the Tradier Account API. 92 | 93 | This function makes a GET request to the Tradier Account API to retrieve the current quote 94 | data for a specified symbol. 95 | 96 | Args: 97 | symbol (str): The trading symbol of the stock (e.g., 'AAPL', 'MSFT') for which you want 98 | to retrieve the current quote data. 99 | last_price (bool, optional): If True, only fetch the last price of the symbol. Default is False. 100 | 101 | Returns: 102 | pandas.DataFrame or float: A DataFrame containing the current quote data for the specified symbol 103 | or just the last price as a float if last_price is set to True. 104 | 105 | Example: 106 | # Retrieve current quote data for symbol 'CCL' and transpose the DataFrame for easy viewing 107 | quote_data = q.get_quote_day(symbol='CCL').T 108 | 109 | Sample Output: 110 | 0 111 | symbol CCL 112 | description Carnival Corp 113 | exch N 114 | type stock 115 | last 15.73 116 | change -0.09 117 | volume 16767253 118 | open 15.83 119 | high 16.06 120 | low 15.58 121 | close 15.73 122 | bid 15.7 123 | ask 15.73 124 | change_percentage -0.57 125 | average_volume 39539044 126 | last_volume 0 127 | trade_date 1693609200001 128 | prevclose 15.82 129 | week_52_high 19.55 130 | week_52_low 6.11 131 | bidsize 11 132 | bidexch P 133 | bid_date 1693612800000 134 | asksize 29 135 | askexch P 136 | ask_date 1693612764000 137 | root_symbols CCL 138 | 139 | # Retrieve only the last price for symbol 'CCL' 140 | last_price = q.get_quote_day(symbol='CCL', last_price=True) 141 | Sample Output: 15.73 142 | ''' 143 | 144 | r = requests.get( 145 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_ENDPOINT), 146 | params = {'symbols':symbol, 'greeks':'false'}, 147 | headers = self.REQUESTS_HEADERS 148 | ); 149 | 150 | df_quote = pd.json_normalize(r.json()['quotes']['quote']); 151 | 152 | if last_price: 153 | return float(df_quote['last']); 154 | 155 | return df_quote; 156 | 157 | def get_timesales (self, symbol, interval='1min', start_time=False, end_time=False): 158 | ''' 159 | This function returns the tick data for `symbol` in increments specified by `interval`. 160 | Eventually, we can use this to analyze/visualze stock data in a time series context. 161 | 162 | Arguments `start_time` and `end_time` must be strings with format 'YYYY-MM-DD HH:MM' 163 | 164 | Sample output: 165 | >>> quotes.get_timesales('VZ', start_time='2023-09-27 09:45', end_time='2023-09-27 14:00') 166 | time timestamp price open high low close volume vwap 167 | 0 2023-09-27T09:45:00 1695822300 32.92995 32.9500 32.9500 32.9099 32.915 39077 32.924828 168 | 1 2023-09-27T09:46:00 1695822360 32.89560 32.9112 32.9112 32.8800 32.895 32867 32.891113 169 | 2 2023-09-27T09:47:00 1695822420 32.88750 32.8950 32.9100 32.8650 32.910 75720 32.888736 170 | 3 2023-09-27T09:48:00 1695822480 32.91750 32.9100 32.9250 32.9100 32.910 15126 32.913109 171 | 4 2023-09-27T09:49:00 1695822540 32.91000 32.9100 32.9200 32.9000 32.920 20335 32.907385 172 | .. ... ... ... ... ... ... ... ... ... 173 | 251 2023-09-27T13:56:00 1695837360 32.35425 32.3450 32.3655 32.3430 32.360 58256 32.354522 174 | 252 2023-09-27T13:57:00 1695837420 32.35500 32.3500 32.3600 32.3500 32.360 15825 32.355307 175 | 253 2023-09-27T13:58:00 1695837480 32.36125 32.3599 32.3700 32.3525 32.365 34624 32.362786 176 | 254 2023-09-27T13:59:00 1695837540 32.37500 32.3700 32.3900 32.3600 32.390 27728 32.370699 177 | 255 2023-09-27T14:00:00 1695837600 32.38750 32.3866 32.3950 32.3800 32.385 53837 32.386281 178 | 179 | (vwap = volume weighted average price during the interval) 180 | ''' 181 | r_params = {'symbol':symbol}; 182 | if start_time: 183 | r_params['start'] = start_time; 184 | if end_time: 185 | r_params['end'] = end_time; 186 | 187 | r = requests.get( 188 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_TIMESALES_ENDPOINT), 189 | params = r_params, 190 | headers = self.REQUESTS_HEADERS 191 | ); 192 | 193 | return pd.json_normalize(r.json()['series']['data']); 194 | 195 | 196 | def search_companies (self, query): 197 | if not query: 198 | return "Need that search term yo"; 199 | 200 | r = requests.get( 201 | url = '{}/{}'.format(self.SANDBOX_URL, self.QUOTES_SEARCH_ENDPOINT), 202 | params = {'q': query, 'indexes':'false'}, 203 | headers = self.REQUESTS_HEADERS 204 | ); 205 | 206 | if not r.json()['securities']: 207 | return "Nothing found"; 208 | 209 | return pd.DataFrame(r.json()['securities']['security']); -------------------------------------------------------------------------------- /Legacy/yahoo.py: -------------------------------------------------------------------------------- 1 | from tradier import * 2 | 3 | import yfinance as yf; 4 | 5 | 6 | # 7 | # Fetch OLHCV bar data for Conoco-Phillips 8 | # 9 | 10 | ticker = yf.Ticker('COP'); 11 | ticker_data = ticker.history(period='1d', start='2023-09-01', end='2023-10-01'); 12 | 13 | print(ticker_data); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uvatradier 2 | 3 | `uvatradier` is a Python package that serves as a wrapper for the Tradier brokerage API. This package simplifies the process of making API requests, handling responses, and performing various trading and account management operations. 4 | 5 | The package was originally developed by data science graduate students at the University of Virginia. 6 | 7 | Wahoowa. 8 | 9 | ## Features 10 | 11 | - **Account Management**: Retrieve profile, balance, gain/loss, orders, and positions related to a trading account. 12 | - **Trading**: Place, modify, and cancel equity and option orders. 13 | - **Market Data**: Access real-time and historical market data for equities and options. 14 | - **Options Data**: Retrieve options chains, strikes, and expiration dates. 15 | - **Stream Market Events**: Stream market events via WebSocket. 16 | 17 | ## Installation 18 | 19 | To install `uvatradier`, you can use `pip`: 20 | 21 | `pip install uvatradier` 22 | 23 | ## Hello World 24 | 25 | Steps to get started: 26 | * Create an account with Tradier.com. 27 | * Grab Account Number and Access Token from your Tradier dashboard. 28 | * Log into Tradier > Preferences (gear icon) > API Access (left panel) > Account Access (section). 29 | * Create a `.env` file in the appropriate working directory on your computer. 30 | * Launch text editor application. 31 | * Save the file as `.env`. 32 | * In the file, add the following lines, replacing the appropriate placeholder fields:
33 | `tradier_acct=` 34 | 35 | `tradier_token=` 36 | * Within the aforementioned working directory, initiate a new python session (interactive python, jupyter notebook, etc.). 37 | * To authenticate yourself, add the following lines of code to the top of your python script or jupyter notebook:
38 | `import os, dotenv` 39 | 40 | `from uvatradier import Tradier, Account, Quotes, EquityOrder, OptionsData, OptionsOrder, Stream` 41 | 42 | `dotenv.load_dotenv()` 43 | 44 | `tradier_acct = os.getenv('tradier_acct')` 45 | 46 | `tradier_token = os.getenv('tradier_token')` 47 | 48 | * To instantiate the class objects, use:
49 | `account = Account(tradier_acct, tradier_token)` 50 | 51 | `quotes = Quotes(tradier_acct, tradier_token)` 52 | 53 | `equity_order = EquityOrder(tradier_acct, tradier_token)` 54 | 55 | `options_data = OptionsData(tradier_acct, tradier_token)` 56 | 57 | `options_order = OptionsOrder(tradier_acct, tradier_token)` 58 | 59 | * To enable live trading: 60 | * Modify the instantiation statements to include a third argument, `live_trade=True`. 61 | * Ensure that the `tradier_acct` and `tradier_token` variables correspond to your production account.
62 | 63 | `tradier = Tradier(tradier_acct, tradier_token, live_trade=True)` 64 | 65 | `account = Account(tradier_acct, tradier_token, live_trade=True)` 66 | 67 | * If live trading is enabled, then you can utilize the `Stream` class to stream market events: 68 | 69 | `stream = Stream(tradier_acct, tradier_token, live_trade=True)` 70 | 71 | ## Usage 72 | 73 | This section provides examples of the most basic functionality. The examples include the minimal arguments needed for a valid function call.
74 | 75 | For most of the class methods, additional examples can be found in their docstring (for example, `help(Quotes.get_historical_quotes)`).
76 | 77 | You can also check the `/examples/` directory if you're really desparate, but it is not (and has not been) updated in awhile. 78 | 79 | ### Account Management 80 | 81 | - Get User Profile: 82 | 83 | `user_profile = account.get_user_profile()` 84 | 85 | - Get Account Balance: 86 | 87 | `account_balance = account.get_account_balance()` 88 | 89 | - Get Gain/Loss: 90 | 91 | `gain_loss = account.get_gainloss()` 92 | 93 | - Get Orders: 94 | 95 | `orders = account.get_orders()` 96 | 97 | - Get Positions: 98 | 99 | `positions = account.get_positions()` 100 | 101 | ### Trading 102 | 103 | - Place Equity Order: 104 | 105 | `order_response = equity_order.order(symbol='GOOD', side='buy', quantity=1, order_type='market', duration='day')` 106 | 107 | - Place Options Order: 108 | 109 | `order_response = options_order.options_order(occ_symbol='GOOD241018C00015000', order_type='market', side='buy_to_open', quantity=1, duration='day')` 110 | 111 | ### Market Data 112 | 113 | - Get Quote for Single Stock: 114 | 115 | `stock_quote = quotes.get_quote_day('TAP')` 116 | 117 | - Get Historical Quotes: 118 | 119 | `historical_quotes = quotes.get_historical_quotes('TAP')` 120 | 121 | - Get Time Sales: 122 | 123 | `timesales = quotes.get_timesales('TAP')` 124 | 125 | - Get Quote for Multiple (List of) Stocks: 126 | 127 | `quote_data = quotes.get_quote_data(['TAP', 'BUD', 'SAM'])` 128 | 129 | ### Options Data 130 | 131 | - Get a Stock's Option Chain: 132 | 133 | `option_chain = options_data.get_chain_day('FUN')` 134 | 135 | - Get Nearest Expiry Date to a Specified Number of Days into Future: 136 | 137 | `option_closest_expiry = options_data.get_closest_expiry('FUN', num_days=45)` 138 | 139 | - Get Future Expiry Dates: 140 | 141 | `option_expiry_dates = options_data.get_expiry_dates('FUN')` 142 | 143 | - Get All OCC Symbols for a Stock: 144 | 145 | `option_symbols = options_data.get_options_symbols('FUN')` 146 | 147 | ### Stream Market Events 148 | 149 | - Start market event stream for JP Morgan Chase: 150 | 151 | `stream.stream_market_events(symbol_list=['JPM'])` 152 | 153 | - Stream Exxon Mobil and Kinder Morgan market events and filter for quote updates and trade events only with a line break to separate each new event: 154 | 155 | `stream.stream_market_events(symbol_list=['XOM', 'KMI'], filter_list=['trade', 'quote'], line_break=True)` 156 | 157 | ## Development 158 | 159 | To contribute or make changes to the `uvatradier` package, feel free to create a fork, clone the fork, make some improvements and issue a pull request. From the terminal/command prompt: 160 | 161 | - Clone the forked branch to your local machine: 162 | 163 | `git clone https://github.com/YOUR_USERNAME/uvatradier.git` 164 | 165 | - Navigate to the directory where the relevant code is kept: 166 | 167 | `cd uvatradier` 168 | 169 | - If you need to be careful about dependencies, create a new Python virtual environment:
170 | 171 | `python -m venv replace_this_with_desired_venv_name` [Mac]
172 | 173 | `venv\Scripts\activate` [Windows] 174 | 175 | ## Questions? 176 | 177 | Happy to help! Feel free to contact Tom Hammons by email at qje5vf@virginia.edu.
178 | Thanks much in advance for your interest in using the package. 179 | 180 | ## License 181 | 182 | `uvatradier` is licensed under the Apache License 2.0 183 | -------------------------------------------------------------------------------- /examples/account_ex.py: -------------------------------------------------------------------------------- 1 | # 2 | # Example - Using uvatradier to fetch account balance information 3 | # 4 | 5 | import os, dotenv; 6 | from uvatradier import Tradier, Account; 7 | 8 | # Create .env file within current working directory. 9 | # Add the following to your .env file. Specify the correct account number and authorization token for your account. 10 | # tradier_acct = 11 | # tradier_token = 12 | 13 | dotenv.load_dotenv(); 14 | 15 | tradier_acct = os.getenv('tradier_acct'); 16 | tradier_token = os.getenv('tradier_token'); 17 | 18 | # Initialize a new Account object 19 | account = Account(tradier_acct, tradier_token); 20 | 21 | # Fetch account balance info 22 | balance = account.get_account_balance(); 23 | 24 | print(balance); 25 | -------------------------------------------------------------------------------- /examples/demo.py: -------------------------------------------------------------------------------- 1 | # 2 | # This script demonstrates how to: 3 | # • Read account number and authorization token from .env file in working directory 4 | # • Instantiate uvatradier class objects for Account, Quotes, and OptionsData 5 | # 6 | 7 | import os, dotenv; 8 | from uvatradier import Tradier, Account, Quotes, OptionsData; 9 | 10 | # 11 | # Check that .env file exists 12 | # 13 | 14 | dotenv.load_dotenv(); 15 | if not dotenv.load_dotenv(): 16 | quit(); 17 | 18 | 19 | # 20 | # Fetch account number and auth token 21 | # 22 | 23 | tradier_acct = os.getenv('tradier_acct'); 24 | tradier_token = os.getenv('tradier_token'); 25 | 26 | 27 | # 28 | # Instantiate uvatradier class objects 29 | # 30 | 31 | account = Account(tradier_acct, tradier_token); 32 | quotes = Quotes(tradier_acct, tradier_token); 33 | options = OptionsData(tradier_acct, tradier_token); 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /examples/quotes_ex.py: -------------------------------------------------------------------------------- 1 | # 2 | # Example - Using uvatradier to fetch quote data for a given symbol 3 | # 4 | 5 | import os, dotenv 6 | from uvatradier import Tradier, Quotes 7 | 8 | # Create .env file within current working directory. 9 | # Add the following to your .env file. Specify the correct account number and authorization token for your quote. 10 | # tradier_acct = 11 | # tradier_token = 12 | 13 | dotenv.load_dotenv() 14 | 15 | tradier_acct = os.getenv('tradier_acct') 16 | tradier_token = os.getenv('tradier_token') 17 | 18 | # Initializing new Quotes object 19 | quotes = Quotes(tradier_acct, tradier_token); 20 | 21 | test_quote = quotes.get_quote_day('KO') 22 | 23 | print(test_quote) 24 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | with open('README.md', 'r', encoding='utf-8') as fh: 5 | long_description = fh.read(); 6 | 7 | setup( 8 | name='uvatradier', 9 | version='0.4.7', 10 | author='tom hammons', 11 | description='wahoowa', 12 | long_description=long_description, 13 | long_description_content_type='text/markdown', 14 | url='https://github.com/thammo4/uvatradier', 15 | packages=find_packages(), 16 | project_urls={'Bug Tracker':'https://github.com/thammo4/uvatradier/issues'}, 17 | keywords='tradier finance api', 18 | install_requires=[ 19 | 'requests>=2.0', 20 | 'pandas>=1.0', 21 | 'matplotlib>=3.0', 22 | 'websockets>=8.1', 23 | 'asyncio' # asyncio is included in the standard library for Python 3.7 and later - unneeded if using these versions. 24 | ] 25 | ); 26 | -------------------------------------------------------------------------------- /uvatradier/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | from .account import Account 3 | from .quotes import Quotes 4 | from .equity_order import EquityOrder 5 | from .options_data import OptionsData 6 | from .options_order import OptionsOrder 7 | from .stream import Stream 8 | 9 | print('wahoowa') 10 | -------------------------------------------------------------------------------- /uvatradier/base.py: -------------------------------------------------------------------------------- 1 | class Tradier: 2 | def __init__ (self, account_number, auth_token, live_trade=False): 3 | 4 | # 5 | # Define account credentials 6 | # 7 | 8 | self.ACCOUNT_NUMBER = account_number; 9 | self.AUTH_TOKEN = auth_token; 10 | self.REQUESTS_HEADERS = {'Authorization':'Bearer {}'.format(self.AUTH_TOKEN), 'Accept':'application/json'} 11 | 12 | 13 | # 14 | # Define base url for live/paper trading and individual API endpoints 15 | # 16 | 17 | self.LIVETRADE_URL = 'https://api.tradier.com'; 18 | self.SANDBOX_URL = 'https://sandbox.tradier.com'; 19 | self.WEBSOCKET_URL = 'wss://ws.tradier.com'; 20 | 21 | self.BASE_URL = self.LIVETRADE_URL if live_trade else self.SANDBOX_URL; -------------------------------------------------------------------------------- /uvatradier/equity_order.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | import requests 4 | import pandas as pd 5 | 6 | 7 | class EquityOrder (Tradier): 8 | def __init__ (self, account_number, auth_token, live_trade=False): 9 | Tradier.__init__(self, account_number, auth_token, live_trade); 10 | 11 | # 12 | # Order endpoint 13 | # 14 | 15 | self.ORDER_ENDPOINT = f"v1/accounts/{self.ACCOUNT_NUMBER}/orders"; # POST 16 | 17 | def order (self, symbol, side, quantity, order_type, duration='day', limit_price=False, stop_price=False): 18 | ''' 19 | Arguments: 20 | symbol = Stock Ticker Symbol. 21 | side = ['buy', 'buy_to_cover', 'sell', 'sell_short'] 22 | order_type = ['market', 'limit', 'stop', 'stop_limit'] 23 | duration = ['day', 'gtc', 'pre', 'post'] 24 | 25 | Example of how to run: 26 | >>> eo = EquityOrder(ACCOUNT_NUMBER, AUTH_TOKEN) 27 | >>> eo.order(symbol='QQQ', side='buy', quantity=10, order_type='market', duration='gtc'); 28 | {'order': {'id': 8256590, 'status': 'ok', 'partner_id': '3a8bbee1-5184-4ffe-8a0c-294fbad1aee9'}} 29 | ''' 30 | 31 | # 32 | # Define initial requests parameters dictionary whose fields are applicable to all order_type values 33 | # 34 | 35 | r_params = { 36 | 'class' : 'equity', 37 | 'symbol' : symbol, 38 | 'side' : side, 39 | 'quantity' : quantity, 40 | 'type' : order_type, 41 | 'duration' : duration 42 | }; 43 | 44 | # 45 | # If the order_type is limit, stop, or stop_limit --> Set the appropriate limit price or stop price 46 | # 47 | 48 | if order_type.lower() in ['limit', 'stop_limit']: 49 | r_params['price'] = limit_price; 50 | if order_type.lower() in ['stop', 'stop_limit']: 51 | r_params['stop'] = stop_price; 52 | 53 | r = requests.post( 54 | url = '{}/{}'.format(self.BASE_URL, self.ORDER_ENDPOINT), 55 | params = r_params, 56 | headers=self.REQUESTS_HEADERS 57 | ); 58 | 59 | return r.json(); -------------------------------------------------------------------------------- /uvatradier/options_data.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | 3 | import requests 4 | import pandas as pd 5 | import re 6 | from datetime import datetime, timedelta; 7 | 8 | 9 | class OptionsData (Tradier): 10 | def __init__ (self, account_number, auth_token, live_trade=False): 11 | Tradier.__init__(self, account_number, auth_token, live_trade); 12 | 13 | # 14 | # Option data endpoints 15 | # 16 | 17 | self. OPTIONS_STRIKE_ENDPOINT = "v1/markets/options/strikes"; # GET 18 | self. OPTIONS_CHAIN_ENDPOINT = "v1/markets/options/chains"; # GET 19 | self. OPTIONS_EXPIRY_ENDPOINT = "v1/markets/options/expirations"; # GET 20 | self. OPTIONS_SYMBOL_ENDPOINT = "v1/markets/options/lookup"; # GET 21 | 22 | # 23 | # Fetch all option chain data across all available expiries 24 | # 25 | 26 | def get_chain_all (self, symbol): 27 | expiry_dates = self.get_expiry_dates(symbol) 28 | 29 | df = pd.DataFrame() 30 | 31 | for expiry in expiry_dates: 32 | df = pd.concat([df, self.get_chain_day(symbol=symbol, expiry=expiry)]) 33 | 34 | return df 35 | 36 | 37 | 38 | # 39 | # Fetch all option chain data for a single day of contract expirations 40 | # 41 | 42 | def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False): 43 | ''' 44 | This function returns option chain data for a given symbol. 45 | All contract expirations occur on the same expiry date 46 | ''' 47 | 48 | # 49 | # Set the contract expiration date to the nearest valid date 50 | # 51 | 52 | if not expiry: 53 | expiry = self.get_expiry_dates(symbol)[0]; 54 | 55 | # 56 | # Define request object for given symbol and expiration 57 | # 58 | 59 | r = requests.get( 60 | url = f"{self.BASE_URL}/{self.OPTIONS_CHAIN_ENDPOINT}", 61 | params = {'symbol':symbol, 'expiration':expiry, 'greeks':'false'}, 62 | headers = self.REQUESTS_HEADERS 63 | ); 64 | 65 | # 66 | # Convert returned json -> pandas dataframe 67 | # 68 | 69 | option_df = pd.DataFrame(r.json()['options']['option']); 70 | 71 | 72 | # 73 | # Remove columns which have the same value for every row 74 | # 75 | 76 | cols_to_drop = option_df.nunique()[option_df.nunique() == 1].index; 77 | option_df = option_df.drop(cols_to_drop, axis=1); 78 | 79 | # 80 | # Remove columns which have NaN in every row 81 | # 82 | 83 | cols_to_drop = option_df.nunique()[option_df.nunique() == 0].index; 84 | option_df = option_df.drop(cols_to_drop, axis=1); 85 | 86 | 87 | # 88 | # Remove description column because its information is redundant 89 | # 90 | 91 | cols_to_drop = ['description']; 92 | option_df = option_df.drop(cols_to_drop, axis=1); 93 | 94 | 95 | # 96 | # Filter rows per strike_low and strike_high 97 | # 98 | 99 | if strike_low: 100 | option_df = option_df.query('strike >= @strike_low'); 101 | 102 | if strike_high: 103 | option_df = option_df.query('strike <= @strike_high'); 104 | 105 | if strike: 106 | option_df = option_df.query('strike == @strike'); 107 | 108 | if option_type in ['call', 'put']: 109 | option_df = option_df.query('option_type == @option_type'); 110 | 111 | 112 | if option_type: 113 | if option_type in ['call', 'put']: 114 | option_df = option_df.query('option_type == @option_type'); 115 | 116 | # 117 | # Return the resulting dataframe whose rows are individual contracts with expiration `expiry` 118 | # 119 | 120 | return option_df; 121 | 122 | 123 | # 124 | # Helper function to retrieve expiry date nearest to a fixed number of days in the future 125 | # 126 | 127 | def get_closest_expiry (self, symbol, num_days): 128 | ''' 129 | This function returns the maturity date nearest to a fixed number of days into the future. 130 | It is well suited to quickly retrieve the necessary maturity date once you've decided on your trading time-frame. 131 | For example, if you want to trade mid-term contracts, you might set the num_days argument to 30. 132 | 133 | Arguments: 134 | • symbol: string ticker symbol of underlying 135 | • num_days: number of days into the future for which you want the nearest maturity. 136 | Returns: 137 | • [string] 'YYYY-mm-dd' maturity date closest to today+num_days in future. 138 | 139 | Example: 140 | >>> datetime.today().strftime('%Y-%m-%d') 141 | '2024-06-30' 142 | >>> options_data = OptionsData(tradier_acct, tradier_token) 143 | >>> options_data.get_closest_expiry(symbol='XOM', num_days=30) 144 | '2024-08-02' 145 | ''' 146 | expiry_dates = self.get_expiry_dates(symbol); 147 | todays_date = datetime.now(); 148 | future_date = todays_date + timedelta(days=num_days); 149 | expiries_dt = [datetime.strptime(d, "%Y-%m-%d") for d in expiry_dates]; 150 | closest_expiry = min(expiries_dt, key=lambda date: abs(date-future_date)); 151 | 152 | return closest_expiry.strftime("%Y-%m-%d"); 153 | 154 | 155 | 156 | def get_expiry_dates (self, symbol, strikes=False): 157 | ''' 158 | Get the expiry dates for options on a given symbol. 159 | 160 | Args: 161 | symbol (str): The symbol for which to retrieve expiry dates. 162 | strikes (bool, optional): Whether to include strike prices for each expiry date. Defaults to False. 163 | 164 | Returns: 165 | If strikes=False -> returns limst or list of dict: A list of expiry dates in the format 'YYYY-MM-DD'. 166 | If strikes=True -> returns a dict, the value of whose only key, `expiration`, is a list whose elements are dictionaries with dates and strike prices. 167 | 168 | Example: 169 | # Instantiate with account number and authorization token 170 | >>> options_data = OptionsData(tradier_acct, tradier_token) 171 | 172 | # Upcoming expiration dates for Zebra Technologies Corp (ZBRA) 173 | >>> options_data.get_expiry_dates('ZBRA') 174 | ['2024-10-18', '2024-11-15', '2024-12-20', '2025-02-21', '2025-05-16'] 175 | 176 | # Upcoming expiration dates and available strike prices for ZBRA (output manually tidied up for clarity) 177 | >>> options_data.get_expiry_dates('ZBRA', True) 178 | {'expiration': [ 179 | {'date': '2024-10-18', 'strikes': {'strike': [135.0, 140.0, 145.0, 150.0, 155.0, 160.0, 165.0, 170.0, 175.0, 180.0, 185.0, 190.0, 195.0, 200.0, 210.0, 220.0, 230.0, 240.0, 250.0, 260.0, 270.0, 280.0, 290.0, 300.0, 310.0, 320.0, 330.0, 340.0, 350.0, 360.0, 370.0, 380.0, 390.0, 400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0, 500.0, 510.0]}}, 180 | 181 | {'date': '2024-11-15', 'strikes': {'strike': [135.0, 140.0, 145.0, 150.0, 155.0, 160.0, 165.0, 170.0, 175.0, 180.0, 185.0, 190.0, 195.0, 200.0, 210.0, 220.0, 230.0, 240.0, 250.0, 260.0, 270.0, 280.0, 290.0, 300.0, 310.0, 320.0, 330.0, 340.0, 350.0, 360.0, 370.0, 380.0, 390.0, 400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0, 500.0]}}, 182 | 183 | {'date': '2024-12-20', 'strikes': {'strike': [100.0, 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0, 140.0, 145.0, 150.0, 155.0, 160.0, 165.0, 170.0, 175.0, 180.0, 185.0, 190.0, 195.0, 200.0, 210.0, 220.0, 230.0, 240.0, 250.0, 260.0, 270.0, 280.0, 290.0, 300.0, 310.0, 320.0, 330.0, 340.0, 350.0, 360.0, 370.0, 380.0, 390.0, 400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0, 500.0]}}, 184 | 185 | {'date': '2025-02-21', 'strikes': {'strike': [155.0, 160.0, 165.0, 170.0, 175.0, 180.0, 185.0, 190.0, 195.0, 200.0, 210.0, 220.0, 230.0, 240.0, 250.0, 260.0, 270.0, 280.0, 290.0, 300.0, 310.0, 320.0, 330.0, 340.0, 350.0, 360.0, 370.0, 380.0, 390.0, 400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0, 500.0, 520.0, 540.0]}}, 186 | 187 | {'date': '2025-05-16', 'strikes': {'strike': [185.0, 190.0, 195.0, 200.0, 210.0, 220.0, 230.0, 240.0, 250.0, 260.0, 270.0, 280.0, 290.0, 300.0, 310.0, 320.0, 330.0, 340.0, 350.0, 360.0, 370.0, 380.0, 390.0, 400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0, 500.0, 520.0, 540.0, 560.0]}} 188 | ]} 189 | ''' 190 | 191 | try: 192 | r = requests.get( 193 | url = f"{self.BASE_URL}/{self.OPTIONS_EXPIRY_ENDPOINT}", 194 | params = {'symbol':symbol, 'includeAllRoots':True, 'strikes':str(strikes)}, 195 | headers = self.REQUESTS_HEADERS 196 | ); 197 | r.raise_for_status(); 198 | except requests.exceptions.RequestException as e: 199 | raise RuntimeError(f"No expiries for {symbol}: {str(e)}."); 200 | 201 | try: 202 | response_data = r.json()['expirations']; 203 | except KeyError: 204 | raise ValueError(f"API Response Error. No expirations: {r.json()}"); 205 | 206 | if not response_data: 207 | print(f"No expiries: {symbol}"); 208 | return list(); 209 | 210 | if not strikes: 211 | return response_data['date']; 212 | 213 | return response_data; 214 | 215 | if r.status_code != 200: 216 | return 'wtf'; 217 | 218 | expiry_dict = r.json()['expirations']; 219 | 220 | # Without strikes, we can get a list of dates 221 | if strikes == False: 222 | return expiry_dict['date']; 223 | 224 | # Otherwise, return a list whose elements are dicts with format {'date':[list_of_strikes]} 225 | return expiry_dict['expiration']; 226 | 227 | 228 | # 229 | # This function returns a list of symbols. Each element has a format 230 | # COP240216C00115000 231 | # 232 | # i.e. 233 | # 234 | # COP 240216 C 00115000 235 | # 236 | 237 | # ########################## 238 | # TODO (9/27): 239 | # 1. Change get_options_symbols so that it returns the dataframe format by default 240 | # 2. Investigate the KeyError: 'expiration_date' error produced from `options.get_options_symbols('BWA', df=True)` 241 | # • Issue arises because there are two different symbols for BorgWarner Inc. {'BWA', 'BWA1'} 242 | # • Not sure how to fix this problem yet though 243 | # ########################## 244 | 245 | def get_options_symbols (self, symbol, df=False): 246 | ''' 247 | This function provides a convenient wrapper to fetch option symbols 248 | for a specified symbol 249 | 250 | If df=False, then a list of OCC options symbols is returned. 251 | For an n-element list, if i=0,...,(n-1), then every (option_list[i-1], option_list[i]) pair of elements represents a pair of call/put options. 252 | 253 | If df=True, then a pandas.DataFrame object is returned. 254 | Each row of the dataframe represents a single put or call contract. 255 | The first column is the OCC symbol. The subsequent columns are the parsed values of the OCC symbol. 256 | ''' 257 | 258 | # 259 | # Helper function to convert the get_option_symbols list into a dataframe 260 | # 261 | 262 | def symbol_list_to_df (option_list): 263 | ''' 264 | This is a helper function called from get_option_symbols. 265 | It will parse a list of option symbols into their constituent components 266 | For example: 267 | option_symbol 268 | = [underlying_symbol, expiry_date, option_type, option_id] 269 | = [LMT, 240119, C, 00300000] 270 | ''' 271 | parsed_options = [] 272 | 273 | for option in option_list: 274 | match = re.match(r'([A-Z]+)(\d{6})([CP])(\d+)', option) 275 | if match: 276 | root_symbol, expiration_date, option_type, strike_price = match.groups() 277 | parsed_options.append({'symbol':option,'root_symbol':root_symbol, 'expiration_date':expiration_date, 'option_type':option_type, 'strike_price':strike_price}) 278 | return pd.DataFrame(parsed_options); 279 | 280 | 281 | # 282 | # Helper function to turn the option dates into standard date format 283 | # 284 | def parse_option_expiries (expiry_list): 285 | ''' 286 | Helper function to turn the option dates into standard date format 287 | ''' 288 | 289 | formatted_expiries = []; 290 | for x in expiry_list: 291 | formatted_expiries.append(datetime.strptime(x, '%y%m%d').strftime('%Y-%m-%d')); 292 | 293 | return formatted_expiries; 294 | 295 | 296 | r = requests.get( 297 | url = f"{self.BASE_URL}/{self.OPTIONS_SYMBOL_ENDPOINT}", 298 | params = {'underlying':symbol}, 299 | headers = self.REQUESTS_HEADERS 300 | ); 301 | 302 | option_list = r.json()['symbols'][0]['options']; 303 | 304 | if df: 305 | option_df = symbol_list_to_df(option_list); 306 | option_df['expiration_date'] = parse_option_expiries(option_df['expiration_date']); 307 | return option_df; 308 | 309 | return option_list; -------------------------------------------------------------------------------- /uvatradier/stream.py: -------------------------------------------------------------------------------- 1 | from .base import Tradier 2 | import requests; 3 | import time; 4 | import asyncio; 5 | import websockets; 6 | import json; 7 | 8 | class Stream (Tradier): 9 | """ 10 | The Stream class provides a method to connect and stream market data in real time using Tradier's API. 11 | This class extends the Tradier base class and utilizes both HTTP and WebSocket protocols to establish 12 | a connection, send subscription requests, and produce incoming market events. 13 | 14 | Note - Per Tradier's documentation, WebSocket streaming is only available via live trading. 15 | Ergo, when instantiating the class object, you need to supply the third argument live_trade with its value set to True. 16 | 17 | Attributes: 18 | - MARKET_STREAM_ENDPOINT (str): API endpoint for market event streaming. 19 | - ACCOUNT_STREAM_ENDPOINT (str): API endpoint for account event streaming. 20 | - MARKET_EVENTS_STREAM_ENDPOINT (str): API endpoint for specific market events. 21 | 22 | Methods: 23 | - stream_market_events: Initiates market event streaming for specified symbols. 24 | - http_market_stream_connect: Establishes an HTTP connection to get a streaming session ID. 25 | - ws_market_connect: Connects to a WebSocket to receive and handle live market data. 26 | """ 27 | def __init__ (self, account_number, auth_token, live_trade=False): 28 | """ 29 | Initialize a new instance of the Stream class which is used for handling real-time 30 | market data streaming through Tradier's API. 31 | 32 | Parameters: 33 | - account_number (str): The account number associated with the Tradier account. 34 | - auth_token (str): The authorization token for API access. 35 | - live_trade (bool): Flag to indicate if the stream is for live trading or simulation. 36 | """ 37 | Tradier.__init__(self, account_number, auth_token, live_trade); 38 | 39 | # 40 | # Define Streaming Endpoints 41 | # 42 | 43 | self.MARKET_STREAM_ENDPOINT = "v1/markets/events/session"; 44 | self.ACCOUNT_STREAM_ENDPOINT = "v1/accounts/events/session"; 45 | self.MARKET_EVENTS_STREAM_ENDPOINT = "v1/markets/events"; 46 | 47 | 48 | # 49 | # Initiate Market Event Stream 50 | # 51 | 52 | def stream_market_events (self, symbol_list, filter_list=None, line_break=False, valid_ticks_only=True, advanced_details=True, callback=None): 53 | """ 54 | Start asynchronous market event streaming for a list of symbols. 55 | This function wraps the asynchronous connection in a synchronous callable method by using asyncio.run. 56 | Uses asyncio.run to start the websocket connection handling asynchronously. 57 | 58 | Parameters: 59 | - symbol_list (list): List of market symbols (e.g., ['AAPL', 'GOOGL']) to subscribe to. 60 | - filter_list (list, optional): List of event types to filter - (Valid Options: 'trade','quote','summary','timesale','tradex') 61 | - line_break (bool, optional): Whether to include line breaks in the streaming data. 62 | - valid_ticks_only (bool, optional): Whether to receive only valid ticks. 63 | - advanced_details (bool, optional): Whether to receive advanced detail level in data. 64 | 65 | Examples: 66 | stream = Stream(tradier_acct, tradier_token, live_trade=True) 67 | # Stream all events for ConocoPhillips, Advanced Micro Devices, and Boeing 68 | stream.stream_market_events(symbol_list=['COP', 'AMD', 'BA']) 69 | # Stream Kinder Morgan Inc. quotes and trade events with line break separating each event 70 | stream.stream_market_events(symbol_list=['KMI'], filter_list=['trade', 'quote'], line_break=True) 71 | """ 72 | asyncio.run( 73 | self.ws_market_connect(symbol_list, filter_list, line_break, valid_ticks_only, advanced_details, callback) 74 | ) 75 | 76 | 77 | # 78 | # Establish HTTP Connection to Tradier 79 | # 80 | 81 | def http_market_stream_connect (self): 82 | """ 83 | Continuously attempt to establish an HTTP connection to the Tradier market stream endpoint 84 | until a session ID is obtained. Handles reconnection attempts and errors. 85 | 86 | Returns: 87 | - str: A session ID string used for initiating a WebSocket connection. 88 | 89 | Raises: 90 | - RuntimeError: If it fails to obtain the session ID after multiple attempts. 91 | """ 92 | while True: 93 | try: 94 | r = requests.post(url=f"{self.BASE_URL}/{self.MARKET_STREAM_ENDPOINT}", headers=self.REQUESTS_HEADERS); 95 | r.raise_for_status(); 96 | 97 | session_info = r.json(); 98 | 99 | # print("API RESPONSE: ", session_info); 100 | 101 | if 'stream' not in session_info: 102 | print("Error - session_info lacks 'stream'."); 103 | time.sleep(10); 104 | continue; 105 | 106 | if 'sessionid' not in session_info['stream']: 107 | print("Error - 'stream' not in session_info.stream."); 108 | time.sleep(10); 109 | continue; 110 | 111 | return session_info['stream']['sessionid']; 112 | 113 | except requests.RequestException as e: 114 | print(f"API Error: {e}."); 115 | time.sleep(10); 116 | 117 | 118 | # 119 | # Connect to WebSocket Stream 120 | # 121 | 122 | async def ws_market_connect (self, symbol_list, filter_list, line_break, valid_ticks_only, advanced_details, callback=None): 123 | """ 124 | Asynchronously connect to the WebSocket stream endpoint and handle market data events. 125 | This function should not need to be called directly. Use stream_market_events instead. 126 | 127 | Parameters: 128 | - symbol_list (list): Symbols for which market data is requested. 129 | - filter_list (list): Specific data event types to filter. 130 | - line_break (bool): Whether to append line breaks in the received data. 131 | - valid_ticks_only (bool): Filter to only valid tick events. 132 | - advanced_details (bool): Request for advanced details in the market data. 133 | 134 | Once connected, sends the specified parameters as a payload and listens for incoming messages. 135 | """ 136 | session_id = self.http_market_stream_connect(); 137 | try: 138 | print(f"Connecting to websocket at {self.WEBSOCKET_URL}/{self.MARKET_EVENTS_STREAM_ENDPOINT} with sessionid {session_id}"); 139 | async with websockets.connect(uri=f"{self.WEBSOCKET_URL}/{self.MARKET_EVENTS_STREAM_ENDPOINT}", ssl=True, compression=None) as websocket: 140 | payload_json = { 141 | 'symbols': symbol_list, 142 | 'sessionid': session_id, 143 | 'linebreak': line_break, 144 | 'advancedDetails': advanced_details 145 | }; 146 | if filter_list is not None and isinstance(filter_list, list): 147 | payload_json['filter'] = filter_list; 148 | 149 | payload = json.dumps(payload_json); 150 | 151 | print(f"Sending: {payload}\n"); 152 | 153 | await websocket.send(payload); 154 | 155 | async for message in websocket: 156 | callback(message) if callback is not None else print(message) 157 | 158 | except websockets.ConnectionClosedError as e: 159 | print(f"Websocket connection closed but idk why: {e}."); 160 | except websockets.WebSocketException as e: 161 | print(f"WebSocket error: {e}."); 162 | except Exception as e: 163 | print(f"Unexpected error: {e}."); --------------------------------------------------------------------------------