Skip to content

Conversation

@Deigue
Copy link
Contributor

@Deigue Deigue commented Oct 30, 2025

  1. Adds support for INSTITUTION_TRANSFER_INTENT - TRANSFER_OUT
    Unfortunately, there is no supporting information (even in the web-interface, not even amounts) when you transfer-out.
    As you can see for yourself:
Unknown activity: {'accountId': '<redacted>', 'aftOriginatorName': None, 'aftTransactionCategory': None, 'aftTransactionType': None, 'amount': None, 'amountSign': 'negative', 'assetQuantity': None, 'assetSymbol': None, 'canonicalId': '<redacted>', 'currency': None, 'eTransferEmail': None, 'eTransferName': None, 'externalCanonicalId': '<redacted>', 'identityId': None, 'institutionName': 'Some Other Broker', 'occurredAt': '<redacted>', 'p2pHandle': None, 'p2pMessage': None, 'spendMerchant': None, 'securityId': None, 'billPayCompanyName': None, 'billPayPayeeNickname': None, 'redactedExternalAccountNumber': None, 'opposingAccountId': None, 'status': None, 'subType': 'TRANSFER_OUT', 'type': 'INSTITUTIONAL_TRANSFER_INTENT', 'strikePrice': None, 'contractType': None, 'expiryDate': None, 'chequeNumber': None, 'provisionalCreditAmount': None, 'primaryBlocker': None, 'interestRate': None, 'frequency': None, 'counterAssetSymbol': None, 'rewardProgram': None, 'counterPartyCurrency': None, 'counterPartyCurrencyAmount': None, 'counterPartyName': None, 'fxRate': None, 'fees': None, 'reference': None, '__typename': 'ActivityFeedItem', 'description': 'INSTITUTIONAL_TRANSFER_INTENT: TRANSFER_OUT'}

So I added a description with atleast whatever was provided with the activity, leading to:

- [<redacted>] [<redacted>] Institutional transfer: transfer to Some Other Broker.  -None None
  1. get_activities While there is bunch more places to add static type hinting, docstrings and fix up, decided to focus on fixing some of the problems seen externally when using the API (with just the sample code from the README.md
Code_Rm13MBvWI4 This happens because on the receiving end the code doesn't know whether it is a dict or a list. Since we already know that `do_graphql_query` will always return a `list` (`get_activities` expects an 'array')

if (expect_type == 'array' and not isinstance(data, list)) or (
expect_type == 'object' and not isinstance(data, dict)):
raise WSApiException(f"GraphQL query failed: {query_name}", response_data)

The changes enforce that the returned type will be list[Any] (still keeps it generic, but we know the shape of the data atleast)
Also can choose to provide multiple account-ids now if the user wants.

image
  1. Minor static type-checking exception for login() , so ended up cleaning that up as well.
Code_NmDh6BmM2y Code_jHamHSiOZq

Rest is just minimal formatting/cleanup, I added some dev/local only dependencies and excluded them from remote.

@Deigue Deigue force-pushed the institution-transfer-out branch from de7edf1 to 5feb4b1 Compare October 30, 2025 00:22
@Deigue
Copy link
Contributor Author

Deigue commented Oct 31, 2025

Let me know if anything needs to be tweaked, have few more changes coming. But its getting tricky to make a new branch and change stuff without it conflicting with this, so I will wait until this gets cleared up.

Anything after this is probably just formatting / less-critical though, unless I actually find something that is missing that I need. So if you wanted, you can publish to pypi after this PR.


I also was curious, I am assuming you apply this library to some of your pet projects. Can you share any short details or insights as to how you ended up using the data/API. Just seeing if I can get any inspiration or ideas for usage. Or perhaps if the dependent projects are open-source, I wanted to see how that looks like.
Currently, I am not doing too much except just automating historical tracking, and setting up a dashboard to calculate cost basis / PnL related stuff on my side, to assert against whats shown on the broker end. And also for automating tax tracking, since we have to do that in Canada.

@gboudreau gboudreau merged commit e5689e7 into gboudreau:main Oct 31, 2025
gboudreau added a commit to gboudreau/ws-api-php that referenced this pull request Oct 31, 2025
…ecifying multiple accounts when calling getActivities()

Ref: gboudreau/ws-api-python#15
@gboudreau
Copy link
Owner

I also was curious, I am assuming you apply this library to some of your pet projects. Can you share any short details or insights as to how you ended up using the data/API. Just seeing if I can get any inspiration or ideas for usage. Or perhaps if the dependent projects are open-source, I wanted to see how that looks like. Currently, I am not doing too much except just automating historical tracking, and setting up a dashboard to calculate cost basis / PnL related stuff on my side, to assert against whats shown on the broker end. And also for automating tax tracking, since we have to do that in Canada.

I'm using it in my own mint-alternative: https://github.com/gboudreau/easymalt (I doubt anyone else but me uses this.)
So I use this library to fetch transactions from my WS checking & savings accounts, and import those transactions into that system, to monitor my income & expenses.

I also have another single-page app that I use to monitor my investments. So I use this library to track transactions in my investments accounts, (buy, sell, div, etc.) and calculate a lot of things, and display graphs and whatnot in that page, to monitor those investments.

@gboudreau
Copy link
Owner

This change is now causing errors on my computer:

  File "/Users/gb/git/easymalt-local/ws_api/wealthsimple_api.py", line 181, in WealthsimpleAPIBase
    otp_answer: str | None = None,
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'

gb@Guillaumes-MacBook-Pro-M4-Pro:~ $ python -V
Python 3.13.3

@gboudreau
Copy link
Owner

My IDE, PyCharm, complains too:

image

gboudreau added a commit that referenced this pull request Nov 1, 2025
@gboudreau
Copy link
Owner

OK, I fixed those and some more using Optional and Union typing.

@Deigue
Copy link
Contributor Author

Deigue commented Nov 1, 2025

Hmm strange, I wonder why it wasn't getting flagged on my end.

Even though I am not using Pycharm anymore, I didn't get those errors in IDE or runtime.
Looked a bit into it and came across this:

The error "TypeError: unsupported operand type(s) for |: type and NoneType" occurs because the
| operator for type hinting (union types) was introduced in Python 3.10.

But given that your printed python version is already beyond that, perhaps/suspect that the project level python is different or lower ... I wonder why.

@Deigue
Copy link
Contributor Author

Deigue commented Nov 1, 2025

Wonder if it is the setup.py classifier:
i.e:

'Programming Language :: Python :: 3',

I am using uv and pyproject.toml to compile/execute, perhaps my effective python version is different.

(I am ok either way though, I was just trying to figure out why the difference happened. Also I find the prior notation without explicitly specifying Union and Optional much less verbose)

@Deigue
Copy link
Contributor Author

Deigue commented Nov 11, 2025

https://docs.astral.sh/ruff/rules/non-pep604-annotation-optional/

Ah, its definitely using a very old version of Python as the effective one. (probably as part of packaging with setup.py and PyPI)
So either the target version needs to updated to be 3.10+ , or keep at 3, but will encounter problems related to complying with the older version of Python.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants