firstrade 0.0.33__py3-none-any.whl → 0.0.35__py3-none-any.whl

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.
firstrade/account.py CHANGED
@@ -8,6 +8,7 @@ import requests
8
8
  from firstrade import urls
9
9
  from firstrade.exceptions import (
10
10
  AccountResponseError,
11
+ LoginError,
11
12
  LoginRequestError,
12
13
  LoginResponseError,
13
14
  )
@@ -244,12 +245,14 @@ class FTSession:
244
245
  "recipientId": item["recipientId"],
245
246
  "t_token": self.t_token,
246
247
  }
248
+ break
247
249
  elif item["channel"] == "email" and self.email is not None:
248
250
  if self.email == item["recipientMask"]:
249
251
  data = {
250
252
  "recipientId": item["recipientId"],
251
253
  "t_token": self.t_token,
252
254
  }
255
+ break
253
256
  response = self.session.post(urls.request_code(), data=data)
254
257
  elif self.login_json["mfa"] and self.mfa_secret is not None:
255
258
  mfa_otp = pyotp.TOTP(self.mfa_secret).now()
@@ -259,6 +262,11 @@ class FTSession:
259
262
  "t_token": self.t_token,
260
263
  }
261
264
  response = self.session.post(urls.verify_pin(), data=data)
265
+ else:
266
+ raise LoginError(
267
+ "MFA required but no valid MFA method "
268
+ "was provided (pin, email/phone, or mfa_secret)."
269
+ )
262
270
  self.login_json = response.json()
263
271
  if self.login_json["error"] == "":
264
272
  if self.pin or self.mfa_secret is not None:
@@ -385,3 +393,52 @@ class FTAccountData:
385
393
 
386
394
  response = self.session.post(url=urls.cancel_order(), data=data)
387
395
  return response.json()
396
+
397
+ def get_balance_overview(self, account, keywords=None):
398
+ """
399
+ Returns a filtered, flattened view of useful balance fields.
400
+
401
+ This is a convenience helper over `get_account_balances` to quickly
402
+ surface likely relevant numbers such as cash, available cash, and
403
+ buying power without needing to know the exact response structure.
404
+
405
+ Args:
406
+ account (str): Account number to query balances for.
407
+ keywords (list[str], optional): Additional case-insensitive substrings
408
+ to match in keys. Defaults to a sensible set for balances.
409
+
410
+ Returns:
411
+ dict: A dict mapping dot-notated keys to values from the balances
412
+ response where the key path contains any of the keywords.
413
+ """
414
+ if keywords is None:
415
+ keywords = [
416
+ "cash",
417
+ "avail",
418
+ "withdraw",
419
+ "buying",
420
+ "bp",
421
+ "equity",
422
+ "value",
423
+ "margin",
424
+ ]
425
+
426
+ payload = self.get_account_balances(account)
427
+
428
+ filtered = {}
429
+
430
+ def _walk(node, path):
431
+ if isinstance(node, dict):
432
+ for k, v in node.items():
433
+ _walk(v, path + [str(k)])
434
+ elif isinstance(node, list):
435
+ for i, v in enumerate(node):
436
+ _walk(v, path + [str(i)])
437
+ else:
438
+ key_path = ".".join(path)
439
+ low = key_path.lower()
440
+ if any(sub in low for sub in keywords):
441
+ filtered[key_path] = node
442
+
443
+ _walk(payload, [])
444
+ return filtered
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firstrade
3
- Version: 0.0.33
3
+ Version: 0.0.35
4
4
  Summary: An unofficial API for Firstrade
5
5
  Home-page: https://github.com/MaxxRK/firstrade-api
6
- Download-URL: https://github.com/MaxxRK/firstrade-api/archive/refs/tags/0033.tar.gz
6
+ Download-URL: https://github.com/MaxxRK/firstrade-api/archive/refs/tags/0035.tar.gz
7
7
  Author: MaxxRK
8
8
  Author-email: maxxrk@pm.me
9
9
  License: MIT
@@ -13,8 +13,6 @@ Classifier: Intended Audience :: Developers
13
13
  Classifier: Topic :: Internet :: WWW/HTTP :: Session
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.8
17
- Classifier: Programming Language :: Python :: 3.9
18
16
  Classifier: Programming Language :: Python :: 3.10
19
17
  Classifier: Programming Language :: Python :: 3.11
20
18
  Classifier: Programming Language :: Python :: 3.12
@@ -22,8 +20,7 @@ Classifier: Programming Language :: Python :: 3.13
22
20
  Description-Content-Type: text/markdown
23
21
  License-File: LICENSE
24
22
  Requires-Dist: requests
25
- Requires-Dist: beautifulsoup4
26
- Requires-Dist: lxml
23
+ Requires-Dist: pyotp
27
24
  Dynamic: author
28
25
  Dynamic: author-email
29
26
  Dynamic: classifier
@@ -1,11 +1,11 @@
1
1
  firstrade/__init__.py,sha256=fNiWYgSTjElY1MNv0Ug-sVLMTR2z_Ngri_FY7Pekdrw,95
2
- firstrade/account.py,sha256=wLPlQOzgaaBwb8tvYPcWdhfTtaUEtM54iPBl5dPpVHY,14129
2
+ firstrade/account.py,sha256=1SMh3fopaHJnyTtHFsbuHXihz7qqv9m8OReDZ3rk-XM,16066
3
3
  firstrade/exceptions.py,sha256=OrWB83rc33LSxrI7WxXo4o7FcIfmvPSC9bAY8K1pn7U,1886
4
4
  firstrade/order.py,sha256=_b1SnqagwBu7KUmvzSUcp8iMOC3I3k-QDjiDLhlVk7E,8710
5
5
  firstrade/symbols.py,sha256=RH36QLx5v3rKPpBidyJFwGJSDkF5u5f2ILTSZNAGXGQ,7903
6
6
  firstrade/urls.py,sha256=Iw10isyvoqKwiSl3TVuIbos5INZzIEwpln3HcZ7P5aw,2125
7
- firstrade-0.0.33.dist-info/licenses/LICENSE,sha256=wPEQjDqm5zMBmEcZp219Labmq_YIjhudpZiUzyVKaFA,1057
8
- firstrade-0.0.33.dist-info/METADATA,sha256=P5nB38P3U45tTO7kSbuPhuBYUb5DVfXEVcSPKxl55sQ,3367
9
- firstrade-0.0.33.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
10
- firstrade-0.0.33.dist-info/top_level.txt,sha256=tdA8v-KDxU1u4VV6soiNWGBlni4ojv_t_j2wFn5nZcs,10
11
- firstrade-0.0.33.dist-info/RECORD,,
7
+ firstrade-0.0.35.dist-info/licenses/LICENSE,sha256=wPEQjDqm5zMBmEcZp219Labmq_YIjhudpZiUzyVKaFA,1057
8
+ firstrade-0.0.35.dist-info/METADATA,sha256=6nQJDoEBpQBlai_7wzwZiWMqm5r8VWMoL21_2mNdH8M,3238
9
+ firstrade-0.0.35.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
+ firstrade-0.0.35.dist-info/top_level.txt,sha256=tdA8v-KDxU1u4VV6soiNWGBlni4ojv_t_j2wFn5nZcs,10
11
+ firstrade-0.0.35.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5