fidelity-api 0.0.3__tar.gz → 0.0.5__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.3
3
+ Version: 0.0.5
4
4
  Summary: An unofficial API for Fidelity
5
5
  Home-page: https://github.com/kennyboy106/fidelity-api
6
6
  Author: Kenneth Tang
@@ -263,103 +263,131 @@ class FidelityAutomation:
263
263
  }
264
264
  }
265
265
  ```
266
+ None
267
+ If an error occured
266
268
  """
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
269
+ try:
270
+ # Go to positions page
271
+ self.page.wait_for_load_state(state="load")
272
+ self.page.goto("https://digital.fidelity.com/ftgw/digital/portfolio/positions")
329
273
 
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
274
+ # This double wait is necessary. If you remove it, I'll kill you
275
+ self.wait_for_loading_sign()
276
+ self.page.wait_for_timeout(1000)
277
+ self.wait_for_loading_sign()
278
+
279
+ # Download the positions as a csv #
280
+ # See if new UI is present
281
+ new_ui = True
339
282
  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])
283
+ self.page.get_by_role("button", name="Available Actions").click(timeout=2000)
284
+ with self.page.expect_download() as download_info:
285
+ self.page.get_by_role("menuitem", name="Download").click()
286
+ download = download_info.value
287
+ except PlaywrightTimeoutError:
288
+ new_ui = False
289
+ if not new_ui:
290
+ try:
291
+ # Use the old UI
292
+ with self.page.expect_download() as download_info:
293
+ self.page.get_by_label("Download Positions").click()
294
+ download = download_info.value
295
+ except PlaywrightTimeoutError:
296
+ return None
297
+ # Get absolute path to file
298
+ cur = os.getcwd()
299
+ positions_csv = os.path.join(cur, download.suggested_filename)
300
+ # Create a copy to work on with the proper file name known
301
+ download.save_as(positions_csv)
302
+
303
+ csv_file = open(positions_csv, newline="", encoding="utf-8-sig")
304
+
305
+ reader = csv.DictReader(csv_file)
306
+ # Ensure all fields we want are present
307
+ required_elements = [
308
+ "Account Number",
309
+ "Account Name",
310
+ "Symbol",
311
+ "Description",
312
+ "Quantity",
313
+ "Last Price",
314
+ "Last Price Change",
315
+ "Current Value",
316
+ ]
317
+ intersection_set = set(reader.fieldnames).intersection(set(required_elements))
318
+ if len(intersection_set) != len(required_elements):
319
+ raise Exception("Not enough elements in fidelity positions csv")
320
+
321
+ for row in reader:
322
+ # Skip empty rows
323
+ if row["Account Number"] is None:
324
+ continue
325
+ # Last couple of rows have some disclaimers, filter those out
326
+ if "and" in row["Account Number"]:
327
+ break
328
+ # Skip accounts that start with 'Y' (Fidelity managed)
329
+ if row["Account Number"][0] == "Y":
330
+ continue
331
+ # Get the value and remove '$' from it
332
+ cur_val = str(row["Current Value"]).replace("$", "").replace("-", "")
333
+ # Get the last price
334
+ last_price = str(row["Last Price"]).replace("$", "").replace("-", "")
335
+ # Get the last price change
336
+ last_price_change = str(row["Last Price Change"]).replace("$", "")
337
+ # Get quantity
338
+ quantity = str(row["Quantity"]).replace("-", "")
339
+ # Get ticker
340
+ ticker = str(row["Symbol"])
341
+
342
+ # Catch any pending activity with special handling
343
+ if "Pending" in ticker:
344
+ cur_val = last_price_change
345
+ # If the value isn't present, move to next row
346
+ if len(cur_val) == 0:
347
+ continue
348
+ # If the last price isn't available, just use the current value
349
+ if len(last_price) == 0:
350
+ last_price = cur_val
351
+ # If the quantity is missing set it to 1 (For SPAXX or any other cash position)
352
+ if len(quantity) == 0:
353
+ quantity = 1
354
+
355
+ # Check for anything that isn't a number
356
+ try:
357
+ float(cur_val)
358
+ except ValueError:
359
+ cur_val = 0
360
+ try:
361
+ float(last_price)
362
+ except ValueError:
363
+ last_price = 0
364
+ try:
365
+ float(quantity)
366
+ except ValueError:
367
+ quantity = 0
368
+
369
+ # Create list of dictionary for stock found
370
+ stock_list = [create_stock_dict(ticker, float(quantity), float(last_price), float(cur_val))]
371
+ # Try setting in the account dict without overwrite
372
+ if not self.set_account_dict(
373
+ account_num=row["Account Number"],
374
+ balance=float(cur_val),
375
+ nickname=row["Account Name"],
376
+ stocks=stock_list,
377
+ overwrite=False,
378
+ ):
379
+ # If the account exists already, add to it
380
+ self.add_stock_to_account_dict(row["Account Number"], stock_list[0])
356
381
 
357
- # Close the file
358
- csv_file.close()
359
- # Delete the file
360
- os.remove(positions_csv)
382
+ # Close the file
383
+ csv_file.close()
384
+ # Delete the file
385
+ os.remove(positions_csv)
361
386
 
362
- return self.account_dict
387
+ return self.account_dict
388
+ except Exception as e:
389
+ print(f"Error in getAccountInfo: {e}")
390
+ return None
363
391
 
364
392
  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
393
  """
@@ -414,8 +442,8 @@ class FidelityAutomation:
414
442
 
415
443
  # Use the info given
416
444
  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,
445
+ "balance": round(balance, 2) if balance is not None else 0.0,
446
+ "withdrawal_balance": round(withdrawal_balance, 2) if withdrawal_balance is not None else 0.0,
419
447
  "nickname": nickname,
420
448
  "stocks": stocks if stocks is not None else []
421
449
  }
@@ -440,10 +468,10 @@ class FidelityAutomation:
440
468
  if account_num in self.account_dict:
441
469
  if overwrite:
442
470
  self.account_dict[account_num]["stocks"] = [stock]
443
- self.account_dict[account_num]["balance"] = stock["value"]
471
+ self.account_dict[account_num]["balance"] = round(stock["value"], 2)
444
472
  else:
445
473
  self.account_dict[account_num]["stocks"].append(stock)
446
- self.account_dict[account_num]["balance"] += stock["value"]
474
+ self.account_dict[account_num]["balance"] += round(stock["value"], 2)
447
475
  return True
448
476
  return False
449
477
 
@@ -1057,6 +1085,7 @@ class FidelityAutomation:
1057
1085
  available_balance = float(available_balance.replace("$", "").replace(",", ""))
1058
1086
 
1059
1087
  # Check if there's enough balance
1088
+ transfer_amount = round(transfer_amount, 2)
1060
1089
  if transfer_amount > available_balance:
1061
1090
  print(f"Insufficient funds. Available: ${available_balance}, Attempted transfer: ${transfer_amount}")
1062
1091
  return False
@@ -1244,6 +1273,7 @@ class FidelityAutomation:
1244
1273
  signs = [self.page.locator("div:nth-child(2) > .loading-spinner-mask-after").first,
1245
1274
  self.page.locator(".pvd-spinner__mask-inner").first,
1246
1275
  self.page.locator("pvd-loading-spinner").first,
1276
+ self.page.locator(".pvd3-spinner-root > .pvd-spinner__spinner > .pvd-spinner__visual > div > .pvd-spinner__mask-inner").first,
1247
1277
  ]
1248
1278
  for sign in signs:
1249
1279
  sign.wait_for(timeout=timeout, state="hidden")
@@ -1291,6 +1321,7 @@ class FidelityAutomation:
1291
1321
 
1292
1322
  # Click it
1293
1323
  selected_entry.click()
1324
+ self.page.wait_for_timeout(50)
1294
1325
 
1295
1326
  # Click the rename button
1296
1327
  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.3
3
+ Version: 0.0.5
4
4
  Summary: An unofficial API for Fidelity
5
5
  Home-page: https://github.com/kennyboy106/fidelity-api
6
6
  Author: Kenneth Tang
@@ -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.3",
8
+ version="0.0.5",
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
File without changes