fidelity-api 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fidelity-api
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: An unofficial API for Fidelity
5
5
  Home-page: https://github.com/kennyboy106/fidelity-api
6
6
  Author: Kenneth Tang
@@ -21,6 +21,7 @@ gather account positions, nickname accounts, etc.
21
21
 
22
22
  Supports 2FA!
23
23
 
24
+ Made with the help of Jinhui Zhen
24
25
  ## Disclaimer
25
26
  I am not a financial advisor and not affiliated with Fidelity in any way. Use this tool at your own risk. I am
26
27
  not responsible for any losses or damages you may incur by using this project. This tool is provided as-is
@@ -10,6 +10,7 @@ gather account positions, nickname accounts, etc.
10
10
 
11
11
  Supports 2FA!
12
12
 
13
+ Made with the help of Jinhui Zhen
13
14
  ## Disclaimer
14
15
  I am not a financial advisor and not affiliated with Fidelity in any way. Use this tool at your own risk. I am
15
16
  not responsible for any losses or damages you may incur by using this project. This tool is provided as-is
@@ -223,6 +223,8 @@ class FidelityAutomation:
223
223
  -------
224
224
  all_stock_dict (dict)
225
225
  A dict of stocks that the account has.
226
+ The dict is keyed by stocks and only has the quantity.
227
+ `all_stock_dict[stock] = quantity (int)`
226
228
  """
227
229
  if account_number in self.account_dict:
228
230
  all_stock_dict = {}
@@ -253,7 +255,8 @@ class FidelityAutomation:
253
255
  ```
254
256
  {
255
257
  'balance': float: Total account balance
256
- 'type': str: The account nickname or default name
258
+ 'nickname': str: The account nickname or default name
259
+ "withdrawal_balance": Use get_list_of_accounts() to populate
257
260
  'stocks': list: A list of dictionaries for each stock found. The dict has:
258
261
  {
259
262
  'ticker': str: The ticker of the stock held
@@ -263,103 +266,132 @@ class FidelityAutomation:
263
266
  }
264
267
  }
265
268
  ```
269
+ None
270
+ If an error occurred
266
271
  """
267
- # Go to positions page
268
- self.page.wait_for_load_state(state="load")
269
- self.page.goto("https://digital.fidelity.com/ftgw/digital/portfolio/positions")
270
- self.wait_for_loading_sign()
271
-
272
- # Download the positions as a csv
273
- with self.page.expect_download() as download_info:
274
- self.page.get_by_label("Download Positions").click()
275
- download = download_info.value
276
- cur = os.getcwd()
277
- positions_csv = os.path.join(cur, download.suggested_filename)
278
- # Create a copy to work on with the proper file name known
279
- download.save_as(positions_csv)
280
-
281
- csv_file = open(positions_csv, newline="", encoding="utf-8-sig")
282
-
283
- reader = csv.DictReader(csv_file)
284
- # Ensure all fields we want are present
285
- required_elements = [
286
- "Account Number",
287
- "Account Name",
288
- "Symbol",
289
- "Description",
290
- "Quantity",
291
- "Last Price",
292
- "Current Value",
293
- ]
294
- intersection_set = set(reader.fieldnames).intersection(set(required_elements))
295
- if len(intersection_set) != len(required_elements):
296
- raise Exception("Not enough elements in fidelity positions csv")
297
-
298
- for row in reader:
299
- # Skip empty rows
300
- if row["Account Number"] is None:
301
- continue
302
- # Last couple of rows have some disclaimers, filter those out
303
- if "and" in row["Account Number"]:
304
- break
305
- # Skip accounts that start with 'Y' (Fidelity managed)
306
- if row["Account Number"][0] == "Y":
307
- continue
308
- # Get the value and remove '$' from it
309
- val = str(row["Current Value"]).replace("$", "").replace("-", "")
310
- # Get the last price
311
- last_price = str(row["Last Price"]).replace("$", "").replace("-", "")
312
- # Get quantity
313
- quantity = str(row["Quantity"]).replace("-", "")
314
- # Get ticker
315
- ticker = str(row["Symbol"])
316
-
317
- # Don't include this if present
318
- if "Pending" in ticker:
319
- continue
320
- # If the value isn't present, move to next row
321
- if len(val) == 0:
322
- continue
323
- # If the last price isn't available, just use the current value
324
- if len(last_price) == 0:
325
- last_price = val
326
- # If the quantity is missing set it to 1 (For SPAXX or any other cash position)
327
- if len(quantity) == 0:
328
- quantity = 1
272
+ try:
273
+ # Go to positions page
274
+ self.page.wait_for_load_state(state="load")
275
+ self.page.goto("https://digital.fidelity.com/ftgw/digital/portfolio/positions")
329
276
 
330
- # Check for anything that isn't a number
331
- try:
332
- float(val)
333
- except ValueError:
334
- val = 0
335
- try:
336
- float(last_price)
337
- except ValueError:
338
- last_price = 0
277
+ # This double wait is necessary. If you remove it, I'll kill you
278
+ self.wait_for_loading_sign()
279
+ self.page.wait_for_timeout(1000)
280
+ self.wait_for_loading_sign()
281
+
282
+ # Download the positions as a csv #
283
+ # See if new UI is present
284
+ new_ui = True
339
285
  try:
340
- float(quantity)
341
- except ValueError:
342
- quantity = 0
343
-
344
- # Create list of dictionary for stock found
345
- stock_list = [create_stock_dict(ticker, float(quantity), float(last_price), float(val))]
346
- # Try setting in the account dict without overwrite
347
- if not self.set_account_dict(
348
- account_num=row["Account Number"],
349
- balance=float(val),
350
- nickname=row["Account Name"],
351
- stocks=stock_list,
352
- overwrite=False,
353
- ):
354
- # If the account exists already, add to it
355
- self.add_stock_to_account_dict(row["Account Number"], stock_list[0])
286
+ self.page.get_by_role("button", name="Available Actions").click(timeout=8000)
287
+ with self.page.expect_download() as download_info:
288
+ self.page.get_by_role("menuitem", name="Download").click()
289
+ download = download_info.value
290
+ except PlaywrightTimeoutError:
291
+ new_ui = False
292
+ if not new_ui:
293
+ try:
294
+ # Use the old UI
295
+ with self.page.expect_download() as download_info:
296
+ self.page.get_by_label("Download Positions").click(timeout=8000)
297
+ download = download_info.value
298
+ except PlaywrightTimeoutError:
299
+ print("Could not get positions csv")
300
+ return None
301
+ # Get absolute path to file
302
+ cur = os.getcwd()
303
+ positions_csv = os.path.join(cur, download.suggested_filename)
304
+ # Create a copy to work on with the proper file name known
305
+ download.save_as(positions_csv)
306
+
307
+ csv_file = open(positions_csv, newline="", encoding="utf-8-sig")
308
+
309
+ reader = csv.DictReader(csv_file)
310
+ # Ensure all fields we want are present
311
+ required_elements = [
312
+ "Account Number",
313
+ "Account Name",
314
+ "Symbol",
315
+ "Description",
316
+ "Quantity",
317
+ "Last Price",
318
+ "Last Price Change",
319
+ "Current Value",
320
+ ]
321
+ intersection_set = set(reader.fieldnames).intersection(set(required_elements))
322
+ if len(intersection_set) != len(required_elements):
323
+ raise Exception("Not enough elements in fidelity positions csv")
324
+
325
+ for row in reader:
326
+ # Skip empty rows
327
+ if row["Account Number"] is None:
328
+ continue
329
+ # Last couple of rows have some disclaimers, filter those out
330
+ if "and" in row["Account Number"]:
331
+ break
332
+ # Skip accounts that start with 'Y' (Fidelity managed)
333
+ if row["Account Number"][0] == "Y":
334
+ continue
335
+ # Get the value and remove '$' from it
336
+ cur_val = str(row["Current Value"]).replace("$", "").replace("-", "")
337
+ # Get the last price
338
+ last_price = str(row["Last Price"]).replace("$", "").replace("-", "")
339
+ # Get the last price change
340
+ last_price_change = str(row["Last Price Change"]).replace("$", "")
341
+ # Get quantity
342
+ quantity = str(row["Quantity"]).replace("-", "")
343
+ # Get ticker
344
+ ticker = str(row["Symbol"])
345
+
346
+ # Catch any pending activity with special handling
347
+ if "Pending" in ticker:
348
+ cur_val = last_price_change
349
+ # If the value isn't present, move to next row
350
+ if len(cur_val) == 0:
351
+ continue
352
+ # If the last price isn't available, just use the current value
353
+ if len(last_price) == 0:
354
+ last_price = cur_val
355
+ # If the quantity is missing set it to 1 (For SPAXX or any other cash position)
356
+ if len(quantity) == 0:
357
+ quantity = 1
358
+
359
+ # Check for anything that isn't a number
360
+ try:
361
+ float(cur_val)
362
+ except ValueError:
363
+ cur_val = 0
364
+ try:
365
+ float(last_price)
366
+ except ValueError:
367
+ last_price = 0
368
+ try:
369
+ float(quantity)
370
+ except ValueError:
371
+ quantity = 0
372
+
373
+ # Create list of dictionary for stock found
374
+ stock_list = [create_stock_dict(ticker, float(quantity), float(last_price), float(cur_val))]
375
+ # Try setting in the account dict without overwrite
376
+ if not self.set_account_dict(
377
+ account_num=row["Account Number"],
378
+ balance=float(cur_val),
379
+ nickname=row["Account Name"],
380
+ stocks=stock_list,
381
+ overwrite=False,
382
+ ):
383
+ # If the account exists already, add to it
384
+ self.add_stock_to_account_dict(row["Account Number"], stock_list[0])
356
385
 
357
- # Close the file
358
- csv_file.close()
359
- # Delete the file
360
- os.remove(positions_csv)
386
+ # Close the file
387
+ csv_file.close()
388
+ # Delete the file
389
+ os.remove(positions_csv)
361
390
 
362
- return self.account_dict
391
+ return self.account_dict
392
+ except Exception as e:
393
+ print(f"Error in getAccountInfo: {e}")
394
+ return None
363
395
 
364
396
  def set_account_dict(self, account_num: str, balance: float = None, withdrawal_balance: float = None, nickname: str = None, stocks: list = None, overwrite: bool = False):
365
397
  """
@@ -414,8 +446,8 @@ class FidelityAutomation:
414
446
 
415
447
  # Use the info given
416
448
  self.account_dict[account_num] = {
417
- "balance": balance if balance is not None else 0.0,
418
- "withdrawal_balance": withdrawal_balance if withdrawal_balance is not None else 0.0,
449
+ "balance": round(balance, 2) if balance is not None else 0.0,
450
+ "withdrawal_balance": round(withdrawal_balance, 2) if withdrawal_balance is not None else 0.0,
419
451
  "nickname": nickname,
420
452
  "stocks": stocks if stocks is not None else []
421
453
  }
@@ -440,10 +472,10 @@ class FidelityAutomation:
440
472
  if account_num in self.account_dict:
441
473
  if overwrite:
442
474
  self.account_dict[account_num]["stocks"] = [stock]
443
- self.account_dict[account_num]["balance"] = stock["value"]
475
+ self.account_dict[account_num]["balance"] = round(stock["value"], 2)
444
476
  else:
445
477
  self.account_dict[account_num]["stocks"].append(stock)
446
- self.account_dict[account_num]["balance"] += stock["value"]
478
+ self.account_dict[account_num]["balance"] += round(stock["value"], 2)
447
479
  return True
448
480
  return False
449
481
 
@@ -733,10 +765,13 @@ class FidelityAutomation:
733
765
 
734
766
  def transaction(self, stock: str, quantity: float, action: str, account: str, dry: bool = True) -> bool:
735
767
  """
736
- Process an order (transaction) using the dedicated trading page.
768
+ Process an order (transaction) using the dedicated trading page. Support extended hour trading.
737
769
 
738
770
  `NOTE`: If you use this function repeatedly but change the stock between ANY call,
739
- RELOAD the page before calling this
771
+ RELOAD the page before calling this. You can do this like so:
772
+ ```
773
+ FidelityAutomation.page.reload()
774
+ ```
740
775
 
741
776
  For buying:
742
777
  If the price of the security is below $1, it will choose limit order and go off of the last price + a little
@@ -984,7 +1019,6 @@ class FidelityAutomation:
984
1019
  # If new account is found, collect and return
985
1020
  if new_dict_acc not in old_dict:
986
1021
  self.new_account_number = new_dict_acc
987
- print(self.new_account_number)
988
1022
  return True
989
1023
 
990
1024
  # No new account number was found, return false
@@ -1245,6 +1279,7 @@ class FidelityAutomation:
1245
1279
  signs = [self.page.locator("div:nth-child(2) > .loading-spinner-mask-after").first,
1246
1280
  self.page.locator(".pvd-spinner__mask-inner").first,
1247
1281
  self.page.locator("pvd-loading-spinner").first,
1282
+ self.page.locator(".pvd3-spinner-root > .pvd-spinner__spinner > .pvd-spinner__visual > div > .pvd-spinner__mask-inner").first,
1248
1283
  ]
1249
1284
  for sign in signs:
1250
1285
  sign.wait_for(timeout=timeout, state="hidden")
@@ -1259,6 +1294,11 @@ class FidelityAutomation:
1259
1294
  The account number for the account to be nicknamed. Ex: `Z12345678`
1260
1295
  nickname (str)
1261
1296
  The nickname to use
1297
+
1298
+ Returns
1299
+ -------
1300
+ Success (bool)
1301
+ True if successful, false otherwise
1262
1302
  """
1263
1303
  try:
1264
1304
  # Get to summary page
@@ -1291,7 +1331,9 @@ class FidelityAutomation:
1291
1331
  return False
1292
1332
 
1293
1333
  # Click it
1334
+ self.page.wait_for_timeout(500)
1294
1335
  selected_entry.click()
1336
+ self.page.wait_for_timeout(500)
1295
1337
 
1296
1338
  # Click the rename button
1297
1339
  self.page.get_by_role("button", name="Rename").click()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fidelity-api
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: An unofficial API for Fidelity
5
5
  Home-page: https://github.com/kennyboy106/fidelity-api
6
6
  Author: Kenneth Tang
@@ -21,6 +21,7 @@ gather account positions, nickname accounts, etc.
21
21
 
22
22
  Supports 2FA!
23
23
 
24
+ Made with the help of Jinhui Zhen
24
25
  ## Disclaimer
25
26
  I am not a financial advisor and not affiliated with Fidelity in any way. Use this tool at your own risk. I am
26
27
  not responsible for any losses or damages you may incur by using this project. This tool is provided as-is
@@ -5,7 +5,7 @@ with open("README.md", "r") as f:
5
5
 
6
6
  setup(
7
7
  name="fidelity-api",
8
- version="0.0.4",
8
+ version="0.0.6",
9
9
  author="Kenneth Tang",
10
10
  description="An unofficial API for Fidelity",
11
11
  long_description=long_description,
File without changes
File without changes