├── .github
├── dependabot.yml
├── stale.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── pre-commit.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── .pre-commit-config.yaml
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── coverage.svg
├── poetry.lock
├── pyproject.toml
├── stake
├── __init__.py
├── asx
│ ├── __init__.py
│ ├── common.py
│ ├── equity.py
│ ├── funding.py
│ ├── market.py
│ ├── order.py
│ ├── product.py
│ ├── trade.py
│ └── transaction.py
├── client.py
├── common.py
├── constant.py
├── equity.py
├── funding.py
├── fx.py
├── market.py
├── order.py
├── product.py
├── ratings.py
├── trade.py
├── transaction.py
├── user.py
└── watchlist.py
└── tests
├── __init__.py
├── cassettes
├── test_equity
│ ├── test_list_equities[exchange0].yaml
│ └── test_list_equities[exchange1].yaml
├── test_funding
│ ├── test_cash_available[exchange0].yaml
│ ├── test_cash_available[exchange1].yaml
│ ├── test_funds_in_flight[exchange0].yaml
│ ├── test_funds_in_flight[exchange1].yaml
│ ├── test_list_fundings[exchange0-request_0].yaml
│ └── test_list_fundings[exchange1-request_1].yaml
├── test_fx
│ └── test_fx_conversion.yaml
├── test_market
│ ├── test_check_market_status[exchange0].yaml
│ └── test_check_market_status[exchange1].yaml
├── test_order
│ ├── test_brokerage[exchange0].yaml
│ ├── test_brokerage[exchange1].yaml
│ ├── test_cancel_order[exchange0].yaml
│ ├── test_cancel_order[exchange1].yaml
│ ├── test_list_orders[exchange0].yaml
│ └── test_list_orders[exchange1].yaml
├── test_product
│ ├── test_find_products_by_name[exchange0].yaml
│ ├── test_find_products_by_name[exchange1].yaml
│ ├── test_get_product[exchange0-symbols0].yaml
│ └── test_get_product[exchange1-symbols1].yaml
├── test_ratings
│ ├── test_list_ratings.yaml
│ └── test_list_ratings_unknown.yaml
├── test_trade
│ ├── test_limit_buy.yaml
│ ├── test_limit_sell.yaml
│ ├── test_sell[exchange0-request_0].yaml
│ ├── test_sell[exchange1-request_1].yaml
│ ├── test_stop_buy.yaml
│ ├── test_successful_trade[exchange0-request_0].yaml
│ ├── test_successful_trade[exchange1-request_1].yaml
│ ├── test_successful_trade[exchange2-request_2].yaml
│ └── test_successful_trade[exchange3-request_3].yaml
├── test_transaction
│ ├── test_list_transactions[exchange0-request_0].yaml
│ └── test_list_transactions[exchange1-request_1].yaml
└── test_watchlist
│ ├── test_add_to_watchlist.yaml
│ ├── test_create_watchlist[exchange0-symbols0].yaml
│ ├── test_create_watchlist[exchange1-symbols1].yaml
│ ├── test_list_watchlist.yaml
│ └── test_remove_from_watchlist.yaml
├── conftest.py
├── test_client.py
├── test_equity.py
├── test_funding.py
├── test_fx.py
├── test_integration.py
├── test_market.py
├── test_order.py
├── test_product.py
├── test_ratings.py
├── test_trade.py
├── test_transaction.py
└── test_watchlist.py
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | registries:
3 | python-index-pypi-python-org-simple:
4 | type: python-index
5 | url: https://pypi.python.org/simple/
6 | username: "${{secrets.PYTHON_INDEX_PYPI_PYTHON_ORG_SIMPLE_USERNAME}}"
7 | password: "${{secrets.PYTHON_INDEX_PYPI_PYTHON_ORG_SIMPLE_PASSWORD}}"
8 |
9 | updates:
10 | - package-ecosystem: pip
11 | directory: "/"
12 | schedule:
13 | interval: daily
14 | open-pull-requests-limit: 10
15 | ignore:
16 | - dependency-name: pytest-asyncio
17 | versions:
18 | - 0.15.0
19 | - dependency-name: faker
20 | versions:
21 | - 5.8.0
22 | - 6.0.0
23 | - 6.1.1
24 | - 6.3.0
25 | - 6.4.1
26 | - 6.5.0
27 | - 6.5.2
28 | - 6.6.0
29 | - 6.6.1
30 | - 6.6.2
31 | - 6.6.3
32 | - 7.0.1
33 | - 8.0.0
34 | - 8.1.0
35 | - dependency-name: python-dotenv
36 | versions:
37 | - 0.16.0
38 | - 0.17.0
39 | - dependency-name: pytest
40 | versions:
41 | - 6.2.2
42 | - 6.2.3
43 | - dependency-name: pre-commit
44 | versions:
45 | - 2.10.0
46 | - 2.10.1
47 | - 2.11.0
48 | - 2.11.1
49 | - dependency-name: aioresponses
50 | versions:
51 | - 0.7.2
52 | - dependency-name: aiohttp
53 | versions:
54 | - 3.7.4
55 | - 3.7.4.post0
56 | - dependency-name: pydantic
57 | versions:
58 | - "1.8"
59 | - 1.8.1
60 | registries:
61 | - python-index-pypi-python-org-simple
62 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 10
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 2
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [master]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [master]
20 | schedule:
21 | - cron: "42 12 * * 1"
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: ["python"]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
37 | # Learn more:
38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
39 |
40 | steps:
41 | - name: Checkout repository
42 | uses: actions/checkout@v2
43 |
44 | # Initializes the CodeQL tools for scanning.
45 | - name: Initialize CodeQL
46 | uses: github/codeql-action/init@v1
47 | with:
48 | languages: ${{ matrix.language }}
49 | # If you wish to specify custom queries, you can do so here or in a config file.
50 | # By default, queries listed here will override any specified in a config file.
51 | # Prefix the list here with "+" to use these queries and those in the config file.
52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
53 |
54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55 | # If this step fails, then you should remove it and run the build manually (see below)
56 | - name: Autobuild
57 | uses: github/codeql-action/autobuild@v1
58 |
59 | # ℹ️ Command-line programs to run using the OS shell.
60 | # 📚 https://git.io/JvXDl
61 |
62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63 | # and modify them (or add more) to build your code if your project
64 | # uses a compiled language
65 |
66 | #- run: |
67 | # make bootstrap
68 | # make release
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@v1
72 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit.yml:
--------------------------------------------------------------------------------
1 | name: pre-commit
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches: [master]
7 |
8 | jobs:
9 | pre-commit:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - uses: actions/checkout@v3
16 | - uses: actions/setup-python@v3
17 | - uses: pre-commit/action@v3.0.0
18 | with:
19 | token: ${{ secrets.GITHUB_TOKEN }}
20 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | release:
9 | strategy:
10 | fail-fast: true
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Set output
15 | id: vars
16 | run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
17 | - uses: actions/setup-python@v2
18 | with:
19 | python-version: 3.7
20 | - name: Run image
21 | uses: abatilo/actions-poetry@v2.0.0
22 | with:
23 | poetry-version: 1.3.2
24 | - name: Publish python package
25 | run: poetry version ${{ steps.vars.outputs.tag }} && poetry config pypi-token.pypi ${{ secrets.PYPI_API_KEY }} && poetry build && poetry publish
26 | - name: Auto commit pyproject.toml
27 | uses: EndBug/add-and-commit@v4.4.0
28 | with:
29 | add: pyproject.toml
30 | branch: master
31 | env:
32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches: [master]
7 |
8 | jobs:
9 | ci:
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
14 | poetry-version: [1.3.2]
15 | os: [ubuntu-latest, macos-latest, windows-latest]
16 | runs-on: ${{ matrix.os }}
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-python@v2
20 | with:
21 | python-version: ${{ matrix.python-version }}
22 | - name: Run image
23 | uses: abatilo/actions-poetry@v2.0.0
24 | with:
25 | poetry-version: ${{ matrix.poetry-version }}
26 | - name: Run Tests
27 | env: # Or as an environment variable
28 | STAKE_TOKEN: ${{ secrets.STAKE_TOKEN }}
29 | run: poetry install && poetry run pytest --cov=stake --cov-report=xml && poetry run coverage-badge -f -o coverage.svg
30 | - name: Upload coverage
31 | uses: actions/upload-artifact@v2
32 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 }}
33 | with:
34 | name: coverage
35 | path: coverage.svg
36 | - name: Auto commit coverage badge
37 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 }}
38 | uses: EndBug/add-and-commit@v4.4.0
39 | env:
40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41 | - name: Run codacy-coverage-reporter
42 | uses: codacy/codacy-coverage-reporter-action@master
43 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 }}
44 | with:
45 | project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
46 | coverage-reports: coverage.xml
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
134 | # pytype static type analyzer
135 | .pytype/
136 |
137 | # Cython debug symbols
138 | cython_debug/
139 |
140 | .vscode
141 | .history
142 |
143 | .env
144 |
145 | STAKE.*.json
146 | .idea
147 | DS_Store
148 | .DS_Store
149 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: "https://github.com/pre-commit/pre-commit-hooks"
3 | rev: v4.3.0
4 | hooks:
5 | - id: trailing-whitespace
6 | - id: end-of-file-fixer
7 | - id: check-yaml
8 | - id: check-added-large-files
9 | - id: requirements-txt-fixer
10 | - repo: "https://github.com/psf/black"
11 | rev: 22.6.0
12 | hooks:
13 | - id: black
14 | - repo: "https://github.com/pre-commit/mirrors-mypy"
15 | rev: v0.961
16 | hooks:
17 | - id: mypy
18 | - repo: "https://github.com/PyCQA/isort"
19 | rev: 5.12.0
20 | hooks:
21 | - id: isort
22 | - repo: "https://github.com/asottile/seed-isort-config"
23 | rev: v2.2.0
24 | hooks:
25 | - id: seed-isort-config
26 | - repo: "https://github.com/myint/docformatter"
27 | rev: v1.4
28 | hooks:
29 | - id: docformatter
30 | args:
31 | - "--in-place"
32 | - repo: https://github.com/charliermarsh/ruff-pre-commit
33 | # Ruff version.
34 | rev: "v0.0.254"
35 | hooks:
36 | - id: ruff
37 | - repo: https://github.com/pre-commit/mirrors-prettier
38 | rev: v3.0.0-alpha.4
39 | hooks:
40 | - id: prettier
41 | types_or: [json, yaml, markdown]
42 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at tabacco.stefano@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | For answers to common questions about this code of conduct, see
74 | https://www.contributor-covenant.org/faq
75 |
--------------------------------------------------------------------------------
/coverage.svg:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "stake"
3 | version = "0.3.0"
4 | readme = "README.md"
5 | homepage = "https://github.com/stabacco/stake-python"
6 | repository = "https://github.com/stabacco/stake-python"
7 | description = "Unofficial https://hellostake.com API wrapper."
8 | authors = ["Stefano Tabacco "]
9 | license = "Apache-2.0"
10 | keywords = ["stake","trading","stocks","financial","python"]
11 |
12 | [tool.poetry.dependencies]
13 | python = ">=3.8,<4.0.0"
14 | python-dotenv = "^0.13.0"
15 | pydantic = "^2.3"
16 | inflection = "^0.5.0"
17 | aiohttp = "^3.9"
18 | single-version = "^1.2.2"
19 |
20 | [tool.poetry.dev-dependencies]
21 | pytest = "^7.2.0"
22 | pytest-asyncio = "^0.14.0"
23 | pre-commit-hooks = "^3.1.0"
24 | pre-commit = "^2.12.0"
25 | pytest-coverage = "^0.0"
26 | black = {version = "^19.10b0", allow-prereleases = true}
27 | pytest-mock = "^3.3.0"
28 | faker = "^4.14.0"
29 | coverage-badge = "^1.0.1"
30 | pytest-recording = "^0.12.0"
31 |
32 | [tool.poetry.group.dev.dependencies]
33 | bump-pydantic = "^0.8.0"
34 |
35 | [tool.isort]
36 | profile = "black"
37 | known_third_party = ["aiohttp", "dotenv", "faker", "inflection", "pydantic", "pytest", "single_version"]
38 | [build-system]
39 | requires = ["poetry>=0.12"]
40 | build-backend = "poetry.masonry.api"
41 |
42 | packages = [
43 | { include = "stake", from="." },
44 | ]
45 |
46 | [tool.ruff]
47 | # Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
48 | select = ["E", "F"]
49 | ignore = ["E501", "E731"]
50 | fixable = ["A", "B", "C", "D", "E", "F"]
51 | unfixable = []
52 |
53 | # Exclude a variety of commonly ignored directories.
54 | exclude = [
55 | ".bzr",
56 | ".direnv",
57 | ".eggs",
58 | ".git",
59 | ".mypy_cache",
60 | ".nox",
61 | ".pants.d",
62 | ".pytype",
63 | ".ruff_cache",
64 | ".tox",
65 | ".venv",
66 | "__pypackages__",
67 | "_build",
68 | "buck-out",
69 | "build",
70 | "dist",
71 | "venv",
72 | ]
73 |
74 | line-length = 100
75 |
76 | # Allow unused variables when underscore-prefixed.
77 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
78 |
79 |
80 | [tool.ruff.mccabe]
81 | # Unlike Flake8, default to a complexity level of 10.
82 | max-complexity = 10
83 |
--------------------------------------------------------------------------------
/stake/__init__.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 | from pathlib import Path
3 |
4 | from single_version import get_version
5 |
6 | from .client import * # noqa: F401, F403
7 | from .common import * # noqa: F401, F403
8 | from .constant import * # noqa: F401, F403
9 | from .funding import * # noqa: F401, F403
10 | from .fx import * # noqa: F401, F403
11 | from .market import * # noqa: F401, F403
12 | from .order import * # noqa: F401, F403
13 | from .product import * # noqa: F401, F403
14 | from .ratings import * # noqa: F401, F403
15 | from .trade import * # noqa: F401, F403
16 | from .transaction import * # noqa: F401, F403
17 | from .watchlist import * # noqa: F401, F403
18 |
19 | __version__ = get_version("stake", Path(__file__).parent.parent)
20 |
--------------------------------------------------------------------------------
/stake/asx/__init__.py:
--------------------------------------------------------------------------------
1 | from . import equity, funding, market, order, product, trade, transaction # noqa
2 | from .common import * # noqa: F401, F403
3 | from .equity import * # noqa: F401, F403
4 | from .funding import * # noqa: F401, F403
5 | from .market import * # noqa: F401, F403
6 | from .order import * # noqa: F401, F403
7 | from .product import * # noqa: F401, F403
8 | from .trade import * # noqa: F401, F403
9 | from .transaction import * # noqa: F401, F403
10 |
--------------------------------------------------------------------------------
/stake/asx/common.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Side(str, Enum):
5 | BUY = "BUY"
6 | SELL = "SELL"
7 |
8 |
9 | class TradeType(str, Enum):
10 | """The type of trade the user is requesting."""
11 |
12 | MARKET: str = "MARKET_TO_LIMIT"
13 | LIMIT: str = "LIMIT"
14 |
--------------------------------------------------------------------------------
/stake/asx/equity.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import BaseModel, ConfigDict, Field
4 |
5 | from stake.common import BaseClient, camelcase
6 |
7 | __all__ = ["EquityPositions"]
8 |
9 |
10 | class EquityPosition(BaseModel):
11 | available_for_trading_qty: Optional[int] = None
12 | average_price: Optional[str] = None
13 | instrument_id: Optional[str] = None
14 | market_value: Optional[str] = None
15 | mkt_price: Optional[str] = None
16 | name: Optional[str] = None
17 | open_qty: Optional[int] = None
18 | prior_close: Optional[str] = None
19 | recent_announcement: Optional[bool] = None
20 | sensitive: Optional[bool] = None
21 | symbol: Optional[str] = None
22 | unrealized_day_pl_percent: Optional[float] = Field(
23 | None, alias="unrealizedDayPLPercent"
24 | )
25 | unrealized_day_pl: Optional[float] = Field(None, alias="unrealizedDayPL")
26 | unrealized_pl_percent: Optional[float] = Field(None, alias="unrealizedPLPercent")
27 | unrealized_pl: Optional[float] = Field(None, alias="unrealizedPL")
28 | model_config = ConfigDict(alias_generator=camelcase)
29 |
30 |
31 | class EquityPositions(BaseModel):
32 | """Represents the user's portforlio, with the list of the currently
33 | available equities."""
34 |
35 | page_num: Optional[int] = None
36 | has_next: Optional[bool] = None
37 | equity_positions: Optional[List[EquityPosition]] = None
38 | model_config = ConfigDict(alias_generator=camelcase)
39 |
40 |
41 | class EquitiesClient(BaseClient):
42 | async def list(self) -> EquityPositions:
43 | """Displays the contents of your portfolio.
44 |
45 | Returns:
46 | EquityPositions: The list of your equities.
47 | """
48 | data = await self._client.get(self._client.exchange.equity_positions)
49 | return EquityPositions(**data)
50 |
--------------------------------------------------------------------------------
/stake/asx/funding.py:
--------------------------------------------------------------------------------
1 | import json
2 | from datetime import datetime
3 | from enum import Enum
4 | from typing import List, Optional
5 | from urllib.parse import urlencode
6 |
7 | from pydantic import BaseModel, ConfigDict, Field
8 | from pydantic.types import UUID
9 |
10 | from stake.asx.transaction import Sort
11 | from stake.common import BaseClient, camelcase
12 |
13 | __all__ = ["FundingRequest", "FundingStatus"]
14 |
15 |
16 | class Action(str, Enum):
17 | DEPOSIT = "DEPOSIT"
18 | DIVIDEND_DEPOSIT = "DIVIDEND_DEPOSIT"
19 | SETTLEMENT = "SETTLEMENT"
20 | TRANSFER = "TRANSFER"
21 | WITHDRAWAL = "WITHDRAWAL"
22 | ADJUSTMENT = "ADJUSTMENT"
23 |
24 |
25 | class Currency(str, Enum):
26 | AUD = "AUD"
27 |
28 |
29 | class FundingSide(str, Enum):
30 | CREDIT = "CREDIT"
31 | DEBIT = "DEBIT"
32 |
33 |
34 | class FundingStatus(str, Enum):
35 | AWAITING_APPROVAL = "AWAITING_APPROVAL"
36 | PENDING = "PENDING"
37 | RECONCILED = "RECONCILED"
38 |
39 |
40 | class FundingRequest(BaseModel):
41 | """Example call:
42 |
43 | t = FundingRequest(
44 | statuses=[FundingStatus.RECONCILED, FundingStatus.PENDING],
45 | actions=[Action.DIVIDEND_DEPOSIT],
46 | sort=[Sort(attribute='insertedAt', direction='asc')])
47 | """
48 |
49 | statuses: Optional[List[FundingStatus]] = Field(
50 | [FundingStatus.RECONCILED], description="Used to filter the results."
51 | )
52 | sort: Optional[List[Sort]] = Field(
53 | None, description="Use this to sort the results."
54 | )
55 | actions: Optional[List[Action]] = Field(
56 | None, description="Used to filter the results. Only works for funding"
57 | )
58 | limit: int = 100
59 | offset: int = 0
60 |
61 | def as_url_params(self) -> str:
62 | """Returns the parameters for the GET request."""
63 | data = json.loads(
64 | self.model_dump_json(
65 | exclude_none=True,
66 | )
67 | )
68 | if data.get("sort"):
69 | data["sort"] = [f"{d['attribute']},{d['direction']}" for d in data["sort"]]
70 |
71 | return (
72 | urlencode(data, doseq=True)
73 | .replace("statuses", "status")
74 | .replace("actions", "action")
75 | .replace("limit", "size")
76 | .replace("offset", "page")
77 | )
78 |
79 |
80 | class FundingRecord(BaseModel):
81 | action: Optional[Action] = None
82 | amount: Optional[float] = None
83 | approved_by: Optional[str] = Field(None, alias="approvedBy")
84 | currency: Optional[Currency] = None
85 | customer_fee: Optional[int] = None
86 | id: Optional[UUID] = None
87 | inserted_at: Optional[datetime] = None
88 | reference: Optional[str] = None
89 | side: Optional[FundingSide] = None
90 | status: Optional[FundingStatus] = None
91 | updated_at: Optional[datetime] = None
92 | user_id: Optional[UUID] = None
93 | model_config = ConfigDict(alias_generator=camelcase)
94 |
95 |
96 | class Fundings(BaseModel):
97 | fundings: Optional[List[FundingRecord]] = Field(None, alias="items")
98 | has_next: Optional[bool] = None
99 | page: Optional[int] = None
100 | total_items: Optional[int] = Field(None, alias="totalItems")
101 | model_config = ConfigDict(alias_generator=camelcase)
102 |
103 |
104 | class CashAvailable(BaseModel):
105 | """Holds information about the cash available in your account."""
106 |
107 | buying_power: Optional[float] = None
108 | cash_available_for_transfer: Optional[float] = None
109 | cash_available_for_withdrawal_hold: Optional[float] = None
110 | cash_available_for_withdrawal: Optional[float] = None
111 | clearing_cash: Optional[float] = None
112 | pending_buys: Optional[int] = None
113 | pending_withdrawals: Optional[int] = None
114 | settled_cash: Optional[float] = None
115 | settlement_hold: Optional[int] = None
116 | trade_settlement: Optional[int] = None
117 | model_config = ConfigDict(alias_generator=camelcase)
118 |
119 |
120 | class FundingsClient(BaseClient):
121 | async def list(self, request: FundingRequest) -> Fundings:
122 | """Returns the funding transactions executed by the user.
123 |
124 | Args:
125 |
126 | request (FundingRequest): the funding request.
127 |
128 | Returns:
129 | Fundings: the list of the fundings retrieved.
130 | """
131 |
132 | data: dict = await self._client.get(
133 | f"{self._client.exchange.transactions}?{request.as_url_params()}"
134 | )
135 |
136 | return Fundings(**data)
137 |
138 | async def in_flight(self) -> Fundings:
139 | """Returns the funds currently in flight."""
140 | request = FundingRequest(
141 | statuses=[FundingStatus.PENDING, FundingStatus.AWAITING_APPROVAL]
142 | )
143 | return await self.list(request=request)
144 |
145 | async def cash_available(self) -> CashAvailable:
146 | data = await self._client.get(self._client.exchange.cash_available)
147 | return CashAvailable(**data)
148 |
--------------------------------------------------------------------------------
/stake/asx/market.py:
--------------------------------------------------------------------------------
1 | from datetime import date
2 | from typing import Optional
3 |
4 | import pydantic
5 | from pydantic import ConfigDict
6 |
7 | from stake.common import BaseClient, camelcase
8 |
9 | __all__ = ["MarketStatus"]
10 |
11 |
12 | class Status(pydantic.BaseModel):
13 | current: Optional[str] = None
14 |
15 |
16 | class MarketStatus(pydantic.BaseModel):
17 | last_trading_date: Optional[date] = None
18 | status: Status
19 | model_config = ConfigDict(alias_generator=camelcase)
20 |
21 |
22 | class MarketClient(BaseClient):
23 | """Retrieves informations about the current status of the market."""
24 |
25 | async def get(self) -> MarketStatus:
26 | data = await self._client.get(self._client.exchange.market_status)
27 | return MarketStatus(**data)
28 |
29 | async def is_open(self) -> bool:
30 | status = await self.get()
31 | return status.status.current == "open"
32 |
--------------------------------------------------------------------------------
/stake/asx/order.py:
--------------------------------------------------------------------------------
1 | from datetime import date, datetime
2 | from typing import List, Optional, Union
3 | from uuid import UUID
4 |
5 | from pydantic import BaseModel, ConfigDict, Field
6 |
7 | from stake.asx.common import TradeType
8 | from stake.asx.transaction import Side
9 | from stake.common import BaseClient, camelcase
10 |
11 | __all__ = ["CancelOrderRequest"]
12 |
13 |
14 | class Order(BaseModel):
15 | average_price: Optional[float] = None
16 | broker: Optional[str] = None
17 | completed_timestamp: Optional[datetime] = None
18 | estimated_brokerage: Optional[float] = None
19 | estimated_exchange_fees: Optional[float] = None
20 | expires_at: Optional[datetime] = None
21 | filled_units: Optional[float] = None
22 | instrument_code: str
23 | instrument_id: Optional[str] = None
24 | limit_price: Optional[float] = None
25 | order_completion_type: Optional[str] = None
26 | order_id: UUID = Field(alias="id")
27 | order_status: Optional[str] = None
28 | placed_timestamp: datetime
29 | side: Side
30 | type: TradeType
31 | units_remaining: Optional[int] = None
32 | validity_date: Optional[Union[date, datetime]] = None
33 | validity: Optional[str] = None
34 | model_config = ConfigDict(alias_generator=camelcase)
35 |
36 |
37 | class Brokerage(BaseModel):
38 | brokerage_fee: Optional[float] = None
39 | brokerage_discount: Optional[float] = None
40 | fixed_fee: Optional[float] = None
41 | variable_fee_percentage: Optional[float] = None
42 | variable_limit: Optional[int] = None
43 | model_config = ConfigDict(alias_generator=camelcase)
44 |
45 |
46 | class CancelOrderRequest(BaseModel):
47 | order_id: str
48 |
49 |
50 | class OrdersClient(BaseClient):
51 | """This client is in charge of dealing with your pending orders.
52 |
53 | These are the orders limit/stop etc.. that have not been traded yet.
54 | """
55 |
56 | async def list(self) -> List[Order]:
57 | """Lists all your pending orders.
58 |
59 | Returns:
60 | List[Order]: The list of pending orders.
61 | """
62 | data = await self._client.get(self._client.exchange.orders)
63 | return [Order(**d) for d in data]
64 |
65 | async def cancel(self, order: Union[Order, CancelOrderRequest]) -> bool:
66 | """Cancels a pending order.
67 |
68 | Args:
69 | order (Union[Order, CancelOrderRequest])): an existing order or its ID.
70 |
71 | Returns:
72 | bool: True if the deletion was succesful.
73 | """
74 | await self._client.post(
75 | self._client.exchange.cancel_order.format(orderId=order.order_id),
76 | payload={},
77 | )
78 | return True
79 |
80 | async def brokerage(self, order_amount: float) -> Brokerage:
81 | """Retrieve the brokerage for an order.
82 |
83 | Args:
84 | order_amount (float): the per unit purchase price
85 | Returns:
86 | Brokerage: ???
87 | """
88 |
89 | data = await self._client.get(
90 | self._client.exchange.brokerage.format(orderAmount=order_amount)
91 | )
92 | return Brokerage(**data)
93 |
--------------------------------------------------------------------------------
/stake/asx/product.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import BaseModel, ConfigDict
4 |
5 | from stake.common import BaseClient, camelcase
6 |
7 | __all__ = ["ProductSearchByName"]
8 |
9 |
10 | class ProductSearchByName(BaseModel):
11 | """Request used to search for Products by their name or description."""
12 |
13 | keyword: str
14 |
15 |
16 | class Instrument(BaseModel):
17 |
18 | instrument_id: str
19 | symbol: str
20 | name: Optional[str] = None
21 | type: str
22 | recent_announcement: Optional[bool] = None
23 | sensitive: Optional[bool] = None
24 | model_config = ConfigDict(alias_generator=camelcase)
25 |
26 |
27 | class Product(BaseModel):
28 | symbol: Optional[str] = None
29 | out_of_market_quantity: Optional[int] = None
30 | out_of_market_surplus: Optional[int] = None
31 | market_status: Optional[str] = None
32 | last_traded_exchange: Optional[str] = None
33 | last_traded_timestamp: Optional[int] = None
34 | last_trade: Optional[str] = None
35 | bid: Optional[float] = None
36 | ask: Optional[float] = None
37 | prior_close: Optional[float] = None
38 | open: Optional[float] = None
39 | high: Optional[float] = None
40 | low: Optional[float] = None
41 | points_change: Optional[float] = None
42 | percentage_change: Optional[float] = None
43 | out_of_market_price: Optional[float] = None
44 | model_config = ConfigDict(alias_generator=camelcase)
45 |
46 |
47 | class ProductsClient(BaseClient):
48 | async def get(self, symbol: str) -> Optional[Product]:
49 | """Given a symbol it will return the matching product.
50 |
51 | Examples:
52 | coles_product = self.get("COL")
53 | """
54 | data = await self._client.get(
55 | self._client.exchange.symbol.format(symbol=symbol)
56 | )
57 |
58 | return Product(**data)
59 |
60 | async def search(self, request: ProductSearchByName) -> List[Instrument]:
61 | products = await self._client.get(
62 | self._client.exchange.products_suggestions.format(keyword=request.keyword)
63 | )
64 | return [Instrument(**product) for product in products["instruments"]]
65 |
66 | async def product_from_instrument(
67 | self, instrument: Instrument
68 | ) -> Optional[Product]:
69 | return await self.get(instrument.symbol)
70 |
--------------------------------------------------------------------------------
/stake/asx/trade.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from enum import Enum
3 | from typing import Optional, Union
4 |
5 | from pydantic import BaseModel, ConfigDict, Field, model_validator, validate_call
6 |
7 | from stake.asx.common import TradeType
8 | from stake.asx.order import Order
9 | from stake.common import BaseClient, camelcase
10 |
11 | __all__ = [
12 | "LimitBuyRequest",
13 | "LimitSellRequest",
14 | "MarketBuyRequest",
15 | "MarketSellRequest",
16 | ]
17 |
18 |
19 | class ExpiryDate(str, Enum):
20 | """The expiry date for the trade."""
21 |
22 | IN_ONE_DAY: str = "GFD"
23 | IN_THIRTY_DAYS: str = "GTC"
24 |
25 |
26 | class GenericTradeRequest(BaseModel):
27 | """Issues a limit buy request."""
28 |
29 | symbol: Optional[str] = Field(
30 | None,
31 | alias="instrumentCode",
32 | description="The symbol for the ",
33 | )
34 | instrument_code: Optional[str] = None
35 |
36 | units: int
37 | validity: ExpiryDate = ExpiryDate.IN_THIRTY_DAYS
38 | validity_date: Optional[datetime] = None
39 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
40 |
41 | @model_validator(mode="after")
42 | @classmethod
43 | def symbol_or_instrument_type(
44 | cls, value: "GenericTradeRequest"
45 | ) -> "GenericTradeRequest":
46 | if value.symbol is None and value.instrument_code is None:
47 | raise ValueError("Either specify symbol or instrument_code")
48 | return value
49 |
50 |
51 | class LimitBuyRequest(GenericTradeRequest):
52 | side: str = "BUY"
53 | type: TradeType = TradeType.LIMIT
54 | price: float
55 |
56 |
57 | class LimitSellRequest(GenericTradeRequest):
58 | side: str = "SELL"
59 | type: TradeType = TradeType.LIMIT
60 | price: float
61 |
62 |
63 | class MarketBuyRequest(GenericTradeRequest):
64 | side: str = "BUY"
65 | type: TradeType = TradeType.MARKET
66 | price: Optional[float] = None
67 |
68 |
69 | class MarketSellRequest(GenericTradeRequest):
70 | side: str = "SELL"
71 | type: TradeType = TradeType.MARKET
72 | price: Optional[float] = None
73 |
74 |
75 | class TradesClient(BaseClient):
76 | """This client is used to buy/sell equities."""
77 |
78 | @validate_call
79 | async def _trade(
80 | self,
81 | url: str,
82 | request: Union[
83 | MarketBuyRequest,
84 | LimitBuyRequest,
85 | LimitSellRequest,
86 | MarketSellRequest,
87 | ],
88 | ) -> Order:
89 | """A generic function used to submit a trade, either buy or sell.
90 |
91 | Args:
92 | url: the url for buy/sell
93 | request:the trade request
94 | Returns:
95 | the Order
96 | """
97 | if not request.instrument_code and request.symbol:
98 | # in this case we need to get the instrument name from the symbol
99 | instrument_id = await self._instrument_id_from_symbol(request.symbol)
100 | request.instrument_code = instrument_id
101 |
102 | data = await self._client.post(
103 | url, payload=request.model_dump(by_alias=True, exclude={"symbol"})
104 | )
105 |
106 | return Order(**data["order"])
107 |
108 | async def _instrument_id_from_symbol(self, symbol: str) -> str:
109 | """Returns the instrument_id from an associated product."""
110 | url = self._client.exchange.instrument_from_symbol.format(symbol=symbol)
111 | data = await self._client.post(url, payload={})
112 | return data["instrumentId"]
113 |
114 | async def buy(self, request: Union[MarketBuyRequest, LimitBuyRequest]) -> Order:
115 | """Creates an order to buy equities.
116 |
117 | Args:
118 | request: the buy request
119 |
120 | Returns:
121 | the Order object
122 | """
123 | # if the price has not been set(in the case of a market order),
124 | # we get the current ask price for that symbol. This seems to
125 | # be what the app is doing, the price value cannot be left null.
126 | if request.price is None:
127 | product = await self._client.products.get(request.symbol)
128 | assert product
129 | assert product.ask
130 | request.price = product.ask
131 |
132 | return await self._trade(self._client.exchange.orders, request)
133 |
134 | async def sell(self, request: Union[MarketSellRequest, LimitSellRequest]) -> Order:
135 | """Creates an order to sell equities.
136 |
137 | Args:
138 | request: the sell request
139 |
140 | Returns:
141 | the Order object
142 | """
143 | # if the price has not been set, we get the current bid price for that symbol.
144 | if request.price is None:
145 | product = await self._client.products.get(request.symbol)
146 | assert product
147 | assert product.bid
148 | request.price = product.bid
149 |
150 | return await self._trade(self._client.exchange.orders, request)
151 |
--------------------------------------------------------------------------------
/stake/asx/transaction.py:
--------------------------------------------------------------------------------
1 | import json
2 | from datetime import date, datetime
3 | from enum import Enum
4 | from typing import List, Optional
5 | from urllib.parse import urlencode
6 |
7 | from pydantic import BaseModel, ConfigDict, Field
8 |
9 | from stake.asx.common import Side
10 | from stake.common import BaseClient, camelcase
11 |
12 | __all__ = ["TransactionRecordRequest", "SortDirection", "Sort"]
13 |
14 |
15 | class SortDirection(str, Enum):
16 | ASC: str = "asc"
17 | DESC: str = "desc"
18 |
19 |
20 | class Sort(BaseModel):
21 | attribute: str
22 | direction: SortDirection
23 |
24 |
25 | class TransactionRecordRequest(BaseModel):
26 | """Example call:
27 |
28 | t = TransactionRecordRequest(sort=[Sort(attribute='insertedAt', direction='asc')])
29 | """
30 |
31 | sort: Optional[List[Sort]] = Field(
32 | None, description="Use this to sort the results."
33 | )
34 |
35 | limit: int = 100
36 | offset: int = 0
37 |
38 | def as_url_params(self) -> str:
39 | """Returns the parameters for the GET request."""
40 | data = json.loads(
41 | self.model_dump_json(
42 | exclude_none=True,
43 | )
44 | )
45 | if data.get("sort", None):
46 | data["sort"] = [f"{d['attribute']},{d['direction']}" for d in data["sort"]]
47 |
48 | return (
49 | urlencode(data, doseq=True)
50 | .replace("limit", "size")
51 | .replace("offset", "page")
52 | )
53 |
54 |
55 | class Transaction(BaseModel):
56 | average_price: Optional[float] = None
57 | broker_order_id: Optional[int] = None
58 | completed_timestamp: Optional[datetime] = None
59 | consideration: Optional[float] = None
60 | contract_note_number: Optional[int] = None
61 | contract_note_numbers: Optional[List[int]] = None
62 | contract_note_received: Optional[bool] = None
63 | effective_price: Optional[float] = None
64 | execution_date: Optional[date] = None
65 | instrument_id: Optional[str] = Field(None, alias="instrumentCode")
66 | limit_price: Optional[float] = None
67 | order_completion_type: Optional[str] = None
68 | order_status: Optional[str] = None
69 | placed_timestamp: Optional[datetime] = None
70 | side: Optional[Side] = None
71 | type: Optional[str] = None
72 | units: Optional[float] = None
73 | user_brokerage_fees: Optional[float] = None
74 | model_config = ConfigDict(alias_generator=camelcase)
75 |
76 |
77 | class Transactions(BaseModel):
78 | transactions: Optional[List[Transaction]] = Field(None, alias="items")
79 | has_next: Optional[bool] = None
80 | page: Optional[int] = None
81 | total_items: Optional[int] = None
82 | model_config = ConfigDict(alias_generator=camelcase)
83 |
84 |
85 | class TransactionsClient(BaseClient):
86 | async def list(self, request: TransactionRecordRequest) -> Transactions:
87 | """Returns the transactions executed by the user.
88 |
89 | Args:
90 | request (TransactionRecordRequest):
91 | used to filter the transactions we want to retrieve
92 |
93 | Returns:
94 | Transactions: The matching transactions
95 | """
96 |
97 | data: dict = await self._client.get(
98 | f"{self._client.exchange.trade_activity}?{request.as_url_params()}"
99 | )
100 |
101 | return Transactions(**data)
102 |
--------------------------------------------------------------------------------
/stake/common.py:
--------------------------------------------------------------------------------
1 | import weakref
2 | from enum import Enum
3 | from functools import partial
4 | from typing import TYPE_CHECKING
5 |
6 | import inflection
7 |
8 | if TYPE_CHECKING: # pragma: no cover
9 | from stake.client import StakeClient
10 |
11 | camelcase = partial(inflection.camelize, uppercase_first_letter=False)
12 |
13 | __all__ = ["SideEnum"]
14 |
15 |
16 | class SideEnum(str, Enum):
17 | BUY = "B"
18 | SELL = "S"
19 |
20 |
21 | class BaseClient:
22 | # flake8: noqa
23 | def __init__(self, client: "StakeClient"):
24 | self._client = weakref.proxy(client)
25 |
--------------------------------------------------------------------------------
/stake/constant.py:
--------------------------------------------------------------------------------
1 | # sourcery skip: use-fstring-for-concatenation
2 | from urllib.parse import urljoin
3 |
4 | from pydantic import BaseModel
5 |
6 | __all__ = ["ASX", "NYSE"]
7 |
8 |
9 | class NYSEUrl(BaseModel):
10 | """Contains all the visited stake urls for the NYSE."""
11 |
12 | STAKE_URL: str = "https://global-prd-api.hellostake.com/api/"
13 | account_balance: str = urljoin(
14 | STAKE_URL, "cma/getAccountBalance", allow_fragments=True
15 | )
16 | account_transactions: str = urljoin(
17 | STAKE_URL, "users/accounts/accountTransactions", allow_fragments=True
18 | )
19 | brokerage: str = urljoin(
20 | STAKE_URL, "orders/brokerage?orderAmount={orderAmount}", allow_fragments=True
21 | )
22 | cancel_order: str = urljoin(
23 | STAKE_URL, "orders/cancelOrder/{orderId}", allow_fragments=True
24 | )
25 | cash_available: str = urljoin(
26 | STAKE_URL, "users/accounts/cashAvailableForWithdrawal", allow_fragments=True
27 | )
28 | create_session: str = urljoin(
29 | STAKE_URL, "sessions/v2/createSession", allow_fragments=True
30 | )
31 | equity_positions: str = urljoin(
32 | STAKE_URL, "users/accounts/v2/equityPositions", allow_fragments=True
33 | )
34 | fund_details: str = urljoin(STAKE_URL, "fund/details", allow_fragments=True)
35 | market_status: str = urljoin(STAKE_URL, "utils/marketStatus", allow_fragments=True)
36 | orders: str = urljoin(STAKE_URL, "users/accounts/v2/orders", allow_fragments=True)
37 | products_suggestions: str = urljoin(
38 | STAKE_URL, "products/getProductSuggestions/{keyword}", allow_fragments=True
39 | )
40 | quick_buy: str = urljoin(
41 | STAKE_URL, "purchaseorders/v2/quickBuy", allow_fragments=True
42 | )
43 | quotes: str = urljoin(
44 | STAKE_URL, "quotes/marketData/{symbols}", allow_fragments=True
45 | )
46 | rate: str = urljoin(STAKE_URL, "wallet/rate", allow_fragments=True)
47 | ratings: str = urljoin(
48 | STAKE_URL,
49 | "data/calendar/ratings?tickers={symbols}&pageSize={limit}",
50 | allow_fragments=True,
51 | )
52 | sell_orders: str = urljoin(STAKE_URL, "sellorders", allow_fragments=True)
53 | symbol: str = urljoin(
54 | STAKE_URL,
55 | "products/searchProduct?symbol={symbol}&page=1&max=1",
56 | allow_fragments=True,
57 | )
58 | transaction_history: str = urljoin(
59 | STAKE_URL, "users/accounts/transactionHistory", allow_fragments=True
60 | )
61 | transaction_details: str = urljoin(
62 | STAKE_URL,
63 | (
64 | "users/accounts/transactionDetails?"
65 | "reference={reference}&referenceType={reference_type}"
66 | ),
67 | allow_fragments=True,
68 | )
69 | transactions: str = urljoin(
70 | STAKE_URL, "users/accounts/transactions", allow_fragments=True
71 | )
72 | users: str = urljoin(STAKE_URL, "user", allow_fragments=True)
73 |
74 | # deprecated, use update_watchlist instead
75 | watchlist_modify: str = urljoin(
76 | STAKE_URL, "instruments/addRemoveInstrumentWatchlist", allow_fragments=True
77 | )
78 | # deprecated, use read_watchlist instead
79 | watchlist: str = urljoin(
80 | STAKE_URL, "products/productsWatchlist/{userId}", allow_fragments=True
81 | )
82 |
83 | watchlists: str = "https://api.prd.stakeover.io/us/instrument/watchlists"
84 | create_watchlist: str = "https://api.prd.stakeover.io/us/instrument/watchlist"
85 | read_watchlist: str = (
86 | "https://api.prd.stakeover.io/us/instrument/watchlist/{watchlist_id}"
87 | )
88 | update_watchlist: str = read_watchlist + "/items"
89 |
90 |
91 | NYSE = NYSEUrl()
92 |
93 |
94 | class ASXUrl(BaseModel):
95 | """Contains all the visited stake urls for the ASX."""
96 |
97 | ASX_STAKE_URL: str = "https://global-prd-api.hellostake.com/api/asx/"
98 | brokerage: str = urljoin(
99 | ASX_STAKE_URL,
100 | "orders/brokerage?orderAmount={orderAmount}",
101 | allow_fragments=True,
102 | )
103 | cash_available: str = urljoin(ASX_STAKE_URL, "cash", allow_fragments=True)
104 | cancel_order: str = urljoin(
105 | ASX_STAKE_URL, "orders/{orderId}/cancel", allow_fragments=True
106 | )
107 | equity_positions: str = urljoin(
108 | ASX_STAKE_URL, "instrument/equityPositions", allow_fragments=True
109 | )
110 | market_status: str = "https://early-bird-promo.hellostake.com/marketStatus"
111 |
112 | orders: str = urljoin(ASX_STAKE_URL, "orders", allow_fragments=True)
113 |
114 | products_suggestions: str = urljoin(
115 | ASX_STAKE_URL,
116 | "instrument/search?searchKey={keyword}",
117 | allow_fragments=True,
118 | )
119 |
120 | symbol: str = urljoin(
121 | ASX_STAKE_URL,
122 | "instrument/singleQuote/{symbol}",
123 | allow_fragments=True,
124 | )
125 |
126 | trade_activity: str = urljoin(
127 | ASX_STAKE_URL, "orders/tradeActivity", allow_fragments=True
128 | )
129 | watchlists: str = urljoin(
130 | ASX_STAKE_URL, "instrument/v2/watchlists", allow_fragments=True
131 | )
132 | create_watchlist: str = urljoin(
133 | ASX_STAKE_URL, "instrument/v2/watchlist", allow_fragments=True
134 | )
135 | read_watchlist: str = urljoin(
136 | ASX_STAKE_URL, "instrument/v2/watchlist/{watchlist_id}", allow_fragments=True
137 | )
138 | update_watchlist: str = urljoin(
139 | ASX_STAKE_URL,
140 | "instrument/v2/watchlist/{watchlist_id}/items",
141 | allow_fragments=True,
142 | )
143 | instrument_from_symbol: str = urljoin(
144 | ASX_STAKE_URL, "instrument/view/{symbol}", allow_fragments=True
145 | )
146 | transactions: str = urljoin(ASX_STAKE_URL, "transactions", allow_fragments=True)
147 | users: str = "https://global-prd-api.hellostake.com/api/user"
148 |
149 |
150 | ASX = ASXUrl()
151 |
--------------------------------------------------------------------------------
/stake/equity.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 | from typing import List, Optional
3 | from uuid import UUID
4 |
5 | from pydantic import BaseModel, ConfigDict, Field
6 |
7 | from stake.common import BaseClient, SideEnum, camelcase
8 |
9 | __all__ = ["EquityCategory"]
10 |
11 |
12 | class EquityCategory(str, Enum):
13 | ETF = "ETF"
14 | STOCK = "Stock"
15 |
16 |
17 | class EquityPosition(BaseModel):
18 | ask_price: Optional[float] = None
19 | available_for_trading_qty: float
20 | average_price: float = Field(alias="avgPrice")
21 | bid_price: Optional[float] = None
22 | category: Optional[EquityCategory] = None
23 | cost_basis: float
24 | daily_return_value: float
25 | encoded_name: str
26 | instrument_id: UUID = Field(alias="instrumentID")
27 | last_trade: float
28 | market_price: float = Field(alias="mktPrice")
29 | market_value: float
30 | name: str
31 | open_qty: float
32 | period: str
33 | prior_close: float
34 | return_on_stock: Optional[float] = None
35 | side: SideEnum
36 | symbol: str
37 | unrealized_day_pl_percent: float = Field(alias="unrealizedDayPLPercent")
38 | unrealized_day_pl: float = Field(alias="unrealizedDayPL")
39 | unrealized_pl: float = Field(alias="unrealizedPL")
40 | url_image: str
41 | yearly_return_percentage: Optional[float] = None
42 | yearly_return_value: Optional[float] = None
43 | model_config = ConfigDict(alias_generator=camelcase)
44 |
45 |
46 | class EquityPositions(BaseModel):
47 | """Represents the user's portforlio, with the list of the currently
48 | available equities."""
49 |
50 | equity_positions: List[EquityPosition]
51 | equity_value: float
52 | prices_only: bool
53 | model_config = ConfigDict(alias_generator=camelcase)
54 |
55 |
56 | class EquitiesClient(BaseClient):
57 | async def list(self) -> EquityPositions:
58 | """Displays the contents of your portfolio.
59 |
60 | Returns:
61 | EquityPositions: The list of your equities.
62 | """
63 | data = await self._client.get(self._client.exchange.equity_positions)
64 | return EquityPositions(**data)
65 |
--------------------------------------------------------------------------------
/stake/funding.py:
--------------------------------------------------------------------------------
1 | """Your current fundings."""
2 | import asyncio
3 | import json
4 | from datetime import datetime
5 | from typing import List, Optional
6 |
7 | from pydantic import BaseModel, ConfigDict, Field
8 |
9 | from stake.common import BaseClient, camelcase
10 | from stake.transaction import TransactionHistoryType, TransactionRecordRequest
11 |
12 |
13 | class Funding(BaseModel):
14 | iof: Optional[str] = None
15 | vet: Optional[str] = None
16 | bsb: Optional[str] = None
17 | account_number: Optional[str] = None
18 | insert_date: Optional[datetime] = None
19 | channel: Optional[str] = None
20 | amount_to: Optional[float] = None
21 | amount_from: Optional[float] = None
22 | status: Optional[str] = None
23 | speed: Optional[str] = None
24 | fx_fee: Optional[float] = None
25 | express_fee: Optional[int] = None
26 | total_fee: Optional[float] = None
27 | spot_rate: Optional[float] = None
28 | reference: Optional[str] = None
29 | w8_fee: Optional[int] = None
30 | currency_from: Optional[str] = None
31 | currency_to: Optional[str] = None
32 | model_config = ConfigDict(alias_generator=camelcase)
33 |
34 |
35 | class CashSettlement(BaseModel):
36 | utc_time: datetime
37 | cash: float
38 | model_config = ConfigDict(alias_generator=camelcase)
39 |
40 |
41 | class CashAvailable(BaseModel):
42 | card_hold_amount: float
43 | cash_available_for_trade: float
44 | cash_available_for_withdrawal: float
45 | cash_balance: float
46 | cash_settlement: List[Optional[CashSettlement]]
47 | dw_cash_available_for_withdrawal: float
48 | pending_orders_amount: float
49 | pending_poli_amount: float
50 | pending_withdrawals: float
51 | reserved_cash: float
52 | model_config = ConfigDict(alias_generator=camelcase)
53 |
54 |
55 | class FundsInFlight(BaseModel):
56 | type: str
57 | insert_date_time: str
58 | estimated_arrival_time: str
59 | estimated_arrival_time_us: str = Field(alias="estimatedArrivalTimeUS")
60 | transaction_type: str
61 | to_amount: float
62 | from_amount: float
63 | model_config = ConfigDict(alias_generator=camelcase)
64 |
65 |
66 | class FundingsClient(BaseClient):
67 | async def list(self, request: TransactionRecordRequest) -> List[Funding]:
68 | payload = json.loads(request.model_dump_json(by_alias=True))
69 | # looks like there is no way to pass filter the transactions here
70 | data = await self._client.post(
71 | self._client.exchange.transaction_history, payload=payload
72 | )
73 |
74 | funding_transactions = [
75 | d
76 | for d in data
77 | if d["referenceType"] == TransactionHistoryType.FUNDING.value
78 | ]
79 |
80 | details = await asyncio.gather(
81 | *[
82 | self._client.get(
83 | self._client.exchange.transaction_details.format(
84 | reference=funding_transaction["reference"],
85 | reference_type=funding_transaction["referenceType"],
86 | ),
87 | )
88 | for funding_transaction in funding_transactions
89 | ]
90 | )
91 | return [Funding(**d) for d in details]
92 |
93 | async def in_flight(self) -> List[FundsInFlight]:
94 | """Returns the funds currently in flight."""
95 | data = await self._client.get(self._client.exchange.fund_details)
96 | data = data.get("fundsInFlight")
97 | return [FundsInFlight(**d) for d in data] if data else []
98 |
99 | async def cash_available(self) -> CashAvailable:
100 | data = await self._client.get(self._client.exchange.cash_available)
101 | return CashAvailable(**data)
102 |
--------------------------------------------------------------------------------
/stake/fx.py:
--------------------------------------------------------------------------------
1 | """Currency conversion."""
2 | from enum import Enum
3 |
4 | from pydantic import BaseModel, ConfigDict
5 | from pydantic.types import UUID4
6 |
7 | from stake.common import BaseClient, camelcase
8 |
9 | __all__ = ["FxConversionRequest", "CurrencyEnum"]
10 |
11 |
12 | class CurrencyEnum(str, Enum):
13 | AUD: str = "AUD"
14 | USD: str = "USD"
15 |
16 |
17 | class FxConversionRequest(BaseModel):
18 | from_currency: CurrencyEnum
19 | to_currency: CurrencyEnum
20 | from_amount: float
21 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
22 |
23 |
24 | class FxConversion(BaseModel):
25 | from_currency: CurrencyEnum
26 | to_currency: CurrencyEnum
27 | from_amount: float
28 | to_amount: float
29 | rate: float
30 | quote: UUID4
31 | model_config = ConfigDict(alias_generator=camelcase)
32 |
33 |
34 | class FxClient(BaseClient):
35 | async def convert(
36 | self, currency_conversion_request: FxConversionRequest
37 | ) -> FxConversion:
38 | """Converts from one currency to another."""
39 | data = await self._client.post(
40 | self._client.exchange.rate,
41 | payload=currency_conversion_request.model_dump(by_alias=True),
42 | )
43 | return FxConversion(**data)
44 |
--------------------------------------------------------------------------------
/stake/market.py:
--------------------------------------------------------------------------------
1 | """Checks the market status."""
2 |
3 | from typing import Optional
4 |
5 | from pydantic import BaseModel, ConfigDict, Field
6 |
7 | from stake.common import BaseClient, camelcase
8 |
9 | __all__ = ["MarketStatus"]
10 |
11 |
12 | class Status(BaseModel):
13 | change_at: Optional[str] = Field(None, alias="change_at")
14 | next: Optional[str] = None
15 | current: str
16 | model_config = ConfigDict(alias_generator=camelcase)
17 |
18 |
19 | class MarketStatus(BaseModel):
20 | status: Status
21 | model_config = ConfigDict(alias_generator=camelcase)
22 |
23 |
24 | class MarketClient(BaseClient):
25 | async def get(self) -> MarketStatus:
26 | data = await self._client.get(self._client.exchange.market_status)
27 | return MarketStatus(**data["response"])
28 |
29 | async def is_open(self) -> bool:
30 | status = await self.get()
31 | return status.status.current == "open"
32 |
--------------------------------------------------------------------------------
/stake/order.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from enum import IntEnum
3 | from typing import List, Optional, Union
4 |
5 | from pydantic import BaseModel, ConfigDict, Field
6 |
7 | from stake.common import BaseClient, SideEnum, camelcase
8 |
9 | __all__ = ["OrderTypeEnum", "CancelOrderRequest"]
10 |
11 |
12 | class OrderTypeEnum(IntEnum):
13 | MARKET = 1
14 | LIMIT = 2
15 | STOP = 3
16 |
17 |
18 | class Order(BaseModel):
19 | order_no: str
20 | order_id: str = Field(alias="orderID")
21 | order_cash_amt: int
22 | symbol: str
23 | stop_price: float
24 | side: SideEnum
25 | order_type: OrderTypeEnum
26 | cum_qty: str
27 | limit_price: float
28 | created_when: datetime
29 | order_status: int
30 | order_qty: float
31 | description: str
32 | instrument_id: str = Field(alias="instrumentID")
33 | image_url: str
34 | instrument_symbol: str
35 | instrument_name: str
36 | encoded_name: str
37 | model_config = ConfigDict(alias_generator=camelcase)
38 |
39 |
40 | class Brokerage(BaseModel):
41 | brokerage_fee: Optional[float] = None
42 | fixed_fee: Optional[float] = None
43 | variable_fee_percentage: Optional[float] = None
44 | variable_limit: Optional[int] = None
45 | model_config = ConfigDict(alias_generator=camelcase)
46 |
47 |
48 | class CancelOrderRequest(BaseModel):
49 | order_id: str
50 |
51 |
52 | class OrdersClient(BaseClient):
53 | """This client is in charge of dealing with your pending orders.
54 |
55 | These are the orders limit/stop etc.. that have not been traded yet.
56 | """
57 |
58 | async def list(self) -> List[Order]:
59 | """Lists all your pending orders.
60 |
61 | Returns:
62 | List[Order]: The list of pending orders.
63 | """
64 | data = await self._client.get(self._client.exchange.orders)
65 | return [Order(**d) for d in data]
66 |
67 | async def cancel(self, order: Union[Order, CancelOrderRequest]) -> bool:
68 | """Cancels a pending order.
69 |
70 | Args:
71 | order (Union[Order, CancelOrderRequest])): an existing order or its ID.
72 |
73 | Returns:
74 | bool: True if the deletion was succesful.
75 | """
76 | await self._client.delete(
77 | self._client.exchange.cancel_order.format(orderId=order.order_id)
78 | )
79 | return True
80 |
81 | async def brokerage(self, order_amount: float) -> Brokerage:
82 | """Retrieve the brokerage for an order.
83 |
84 | Args:
85 | order_amount (float): the per unit purchase price
86 | Returns:
87 | Brokerage: the brokerage information
88 | """
89 |
90 | data = await self._client.get(
91 | self._client.exchange.brokerage.format(orderAmount=order_amount)
92 | )
93 | return Brokerage(**data)
94 |
--------------------------------------------------------------------------------
/stake/product.py:
--------------------------------------------------------------------------------
1 | import uuid
2 | from datetime import datetime
3 | from typing import Any, List, Optional
4 |
5 | from pydantic import BaseModel, ConfigDict
6 | from pydantic.fields import Field
7 |
8 | from stake.common import BaseClient, camelcase
9 |
10 | __all__ = ["ProductSearchByName"]
11 |
12 |
13 | class ProductSearchByName(BaseModel):
14 | """Request used to search for Products by their name or description."""
15 |
16 | keyword: str
17 |
18 |
19 | class Instrument(BaseModel):
20 | encoded_name: Optional[str] = None
21 | image_url: Optional[str] = None
22 | instrument_id: str
23 | name: str
24 | symbol: str
25 | model_config = ConfigDict(alias_generator=camelcase)
26 |
27 |
28 | class Product(BaseModel):
29 | id: uuid.UUID
30 | instrument_type_id: Optional[str] = Field(None, alias="instrumentTypeID")
31 | symbol: str
32 | description: str
33 | category: Optional[str] = None
34 | currency_id: Optional[str] = Field(None, alias="currencyID")
35 | url_image: str
36 | sector: Optional[str] = None
37 | parent_id: Optional[str] = Field(None, alias="parentID")
38 | name: str
39 | daily_return: float
40 | daily_return_percentage: float
41 | last_traded: float
42 | monthly_return: float
43 | yearly_return_percentage: Optional[float] = None
44 | yearly_return_value: Optional[float] = None
45 | popularity: int
46 | watched: int
47 | news: int
48 | bought: int
49 | viewed: int
50 | product_type: str
51 | trade_status: Optional[int] = None
52 | encoded_name: str
53 | period: str
54 | inception_date: Optional[datetime] = None
55 | instrument_tags: List[Any]
56 | child_instruments: List[Instrument]
57 | model_config = ConfigDict(alias_generator=camelcase)
58 |
59 |
60 | class ProductsClient(BaseClient):
61 | async def get(self, symbol: str) -> Optional[Product]:
62 | """Given a symbol it will return the matching product.
63 |
64 | Examples:
65 | tesla_product = self.get("TSLA")
66 | """
67 | data = await self._client.get(
68 | self._client.exchange.symbol.format(symbol=symbol)
69 | )
70 |
71 | return Product(**data["products"][0]) if data["products"] else None
72 |
73 | async def search(self, request: ProductSearchByName) -> List[Instrument]:
74 | products = await self._client.get(
75 | self._client.exchange.products_suggestions.format(keyword=request.keyword)
76 | )
77 | return [Instrument(**product) for product in products["instruments"]]
78 |
79 | async def product_from_instrument(
80 | self, instrument: Instrument
81 | ) -> Optional[Product]:
82 | return await self.get(instrument.symbol)
83 |
--------------------------------------------------------------------------------
/stake/ratings.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from typing import List, Optional
3 |
4 | import pydantic
5 |
6 | from stake.common import BaseClient
7 |
8 | __all__ = ["RatingsRequest"]
9 |
10 |
11 | class RatingsRequest(pydantic.BaseModel):
12 | """Request for retrieving the ratings for the given symbols."""
13 |
14 | symbols: List[str]
15 | limit: Optional[int] = 50
16 |
17 |
18 | class Rating(pydantic.BaseModel):
19 | id: Optional[str] = None
20 | symbol: Optional[str] = pydantic.Field(alias="ticker")
21 | exchange: Optional[str] = None
22 | name: Optional[str] = None
23 | analyst: Optional[str] = None
24 | currency: Optional[str] = None
25 | url: Optional[str] = None
26 | importance: Optional[int] = None
27 | notes: Optional[str] = None
28 | updated: Optional[datetime] = None
29 | action_pt: Optional[str] = None
30 | action_company: Optional[str] = None
31 | rating_current: Optional[str] = None
32 | pt_current: Optional[float] = None
33 | rating_prior: Optional[str] = None
34 | pt_prior: Optional[float] = None
35 | url_calendar: Optional[str] = None
36 | url_news: Optional[str] = None
37 | analyst_name: Optional[str] = None
38 |
39 | @pydantic.field_validator("pt_prior", "rating_prior", mode="before")
40 | @classmethod
41 | def pt_prior_blank_string(cls, value, *args) -> Optional[str]:
42 | return None if value == "" else value
43 |
44 |
45 | class RatingsClient(BaseClient):
46 | """This client is in charge listing the experts' ratings for symbols."""
47 |
48 | async def list(self, request: RatingsRequest) -> List[Rating]:
49 | """Lists all the ratings for the symbols specified in the request.
50 |
51 | Returns:
52 | List[Rating]: The list of ratings.
53 | """
54 | data = await self._client.get(
55 | self._client.exchange.ratings.format(
56 | symbols=",".join(request.symbols),
57 | limit=request.limit,
58 | )
59 | )
60 |
61 | if data == {"message": "No data returned"}:
62 | return []
63 | print(data["ratings"])
64 | return [Rating(**d) for d in data["ratings"]]
65 |
--------------------------------------------------------------------------------
/stake/trade.py:
--------------------------------------------------------------------------------
1 | import re
2 | from datetime import datetime
3 | from enum import Enum
4 | from typing import Optional, Union
5 |
6 | from pydantic import BaseModel, ConfigDict, Field, field_validator
7 |
8 | from stake.common import BaseClient, camelcase
9 |
10 | failed_transaction_regex = re.compile(r"^[0-9]{4}")
11 |
12 | __all__ = [
13 | "LimitBuyRequest",
14 | "LimitSellRequest",
15 | "MarketBuyRequest",
16 | "MarketSellRequest",
17 | "StopBuyRequest",
18 | "StopSellRequest",
19 | "TradeType",
20 | ]
21 |
22 |
23 | class TradeType(str, Enum):
24 | """The type of trade the user is requesting."""
25 |
26 | MARKET: str = "market"
27 | LIMIT: str = "limit"
28 | STOP: str = "stop"
29 |
30 |
31 | class MarketBuyRequest(BaseModel):
32 | """This is the request that needs to be passed to the trade client in order
33 | to buy some equity."""
34 |
35 | # AAPL, MSFT, TSLA etc...
36 | symbol: str
37 | amount_cash: float
38 | comments: Optional[str] = None
39 |
40 | item_type: str = "instrument"
41 | order_type: TradeType = TradeType.MARKET
42 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
43 |
44 |
45 | class LimitBuyRequest(BaseModel):
46 | symbol: str
47 | limit_price: float
48 | quantity: int
49 | comments: Optional[str] = None
50 |
51 | item_type: str = "instrument"
52 | order_type: TradeType = TradeType.LIMIT
53 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
54 |
55 |
56 | class StopBuyRequest(BaseModel):
57 | symbol: str
58 | amount_cash: float
59 | price: float # must be higher than the current one
60 | comments: Optional[str] = None
61 |
62 | item_type: str = "instrument"
63 | order_type: TradeType = TradeType.STOP
64 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
65 |
66 | @field_validator("amount_cash")
67 | @classmethod
68 | def at_least_10(cls, v: float) -> float: # noqa
69 |
70 | if v < 10.0:
71 | raise ValueError("'amount_cash' must be at least '10$'.")
72 |
73 | return v
74 |
75 |
76 | class LimitSellRequest(BaseModel):
77 | symbol: str
78 | limit_price: float
79 | quantity: int
80 | comments: Optional[str] = None
81 |
82 | item_type: str = "instrument"
83 | order_type: TradeType = TradeType.LIMIT
84 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
85 |
86 |
87 | class StopSellRequest(BaseModel):
88 | symbol: str
89 | quantity: float
90 | stop_price: float
91 | comments: Optional[str] = None
92 |
93 | item_type: str = "instrument"
94 | order_type: TradeType = TradeType.STOP
95 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
96 |
97 |
98 | class MarketSellRequest(BaseModel):
99 | """Sell at marked price."""
100 |
101 | symbol: str
102 | quantity: float
103 | comments: Optional[str] = None
104 |
105 | item_type: str = "instrument"
106 | order_type: TradeType = TradeType.MARKET
107 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
108 |
109 |
110 | class TradeResponse(BaseModel):
111 | """The response from a request to buy/sell."""
112 |
113 | amount_cash: Optional[float] = None
114 | category: str
115 | commission: Optional[float] = None
116 | description: Optional[str] = None
117 | dw_order_id: str
118 | effective_price: Optional[float] = None
119 | encoded_name: str
120 | id: str
121 | image_url: str = Field(alias="imageURL")
122 | inserted_date: datetime
123 | item_id: str
124 | limit_price: Optional[float] = None
125 | name: str
126 | order_reject_reason: Optional[str] = None
127 | quantity: Optional[float] = None
128 | side: str
129 | status: Optional[int] = None
130 | stop_price: Optional[float] = None
131 | symbol: str
132 | updated_date: datetime
133 | model_config = ConfigDict(alias_generator=camelcase)
134 |
135 |
136 | class TradesClient(BaseClient):
137 | """This client is used to buy/sell equities."""
138 |
139 | async def _trade(
140 | self,
141 | url: str,
142 | request: Union[
143 | MarketBuyRequest,
144 | LimitBuyRequest,
145 | StopBuyRequest,
146 | LimitSellRequest,
147 | StopSellRequest,
148 | MarketSellRequest,
149 | ],
150 | check_success: bool = True,
151 | ) -> TradeResponse:
152 | """A generic function used to submit a trade, either buy or sell.
153 |
154 | Args:
155 | url: the url for buy/sell
156 | request:
157 | check_success: if true, an extra check will be performed to see if
158 | the transaction really occurred. This should not be needed, since it should
159 | be possible to inspect the TradeResponse for errors, but it does not look
160 | like they are recorded anywhere.
161 |
162 | Returns:
163 | the TradeResponse
164 |
165 | Raises:
166 | RuntimeError
167 | """
168 | request_dict = request.model_dump(by_alias=True)
169 | product = await self._client.products.get(request_dict.pop("symbol"))
170 | assert product
171 |
172 | request_dict["orderType"] = request_dict["orderType"].value
173 | request_dict["userId"] = str(self._client.user.id)
174 | request_dict["itemId"] = str(product.id)
175 | data = await self._client.post(url, request_dict)
176 | trade = TradeResponse(**data[0])
177 |
178 | if check_success:
179 | await self._verify_successful_trade(trade)
180 |
181 | return trade
182 |
183 | async def _verify_successful_trade(self, trade: TradeResponse) -> None:
184 | """We check the status of the trade by trying to find the matching
185 | transaction and inspecting its properties. This should not be needed
186 | but there is nothing i can see in the trading response that would help
187 | figuring out if the trade request was successful or not.
188 |
189 | Args:
190 | trade: the responded trade
191 |
192 | Raises:
193 | RuntimeError if the trade was not successful.
194 | """
195 |
196 | transactions = await self._client.get(self._client.exchange.transactions)
197 |
198 | if not transactions:
199 | raise RuntimeError(
200 | "The trade did not succeed (Reason: no transaction found)."
201 | )
202 |
203 | # wait for the transaction to be available
204 | for transaction in transactions["transactions"]:
205 | if transaction["orderId"] == trade.dw_order_id:
206 | if re.search(failed_transaction_regex, transaction["updatedReason"]):
207 | raise RuntimeError(
208 | "The trade did not succeed "
209 | f"(Reason: {transaction['updatedReason']}"
210 | )
211 | else:
212 | return
213 |
214 | raise RuntimeError("Could not find a matching transaction.")
215 |
216 | async def buy(
217 | self, request: Union[MarketBuyRequest, LimitBuyRequest, StopBuyRequest]
218 | ) -> TradeResponse:
219 | """Creates an order to buy equities.
220 |
221 | Args:
222 | request: the buy request
223 |
224 | Returns:
225 | the TradeResponse object
226 | """
227 | return await self._trade(self._client.exchange.quick_buy, request)
228 |
229 | async def sell(self, request: MarketSellRequest) -> TradeResponse:
230 | """Creates an order to sell equities.
231 |
232 | Args:
233 | request: the sell request
234 |
235 | Returns:
236 | the TradeResponse object
237 | """
238 | return await self._trade(self._client.exchange.sell_orders, request)
239 |
--------------------------------------------------------------------------------
/stake/transaction.py:
--------------------------------------------------------------------------------
1 | import enum
2 | import json
3 | from datetime import datetime, timedelta
4 | from enum import Enum
5 | from typing import Dict, List, Optional
6 |
7 | from pydantic import BaseModel, ConfigDict, Field
8 | from pydantic.types import UUID, UUID4
9 |
10 | from stake.common import BaseClient, camelcase
11 |
12 | __all__ = ["TransactionRecordRequest"]
13 |
14 |
15 | class TransactionRecordEnumDirection(str, Enum):
16 | prev: str = "prev"
17 | next_: str = "next"
18 |
19 |
20 | class TransactionRecordRequest(BaseModel):
21 | to: datetime = Field(default_factory=datetime.utcnow)
22 | from_: datetime = Field(
23 | default_factory=lambda *_: datetime.utcnow() - timedelta(days=365), alias="from"
24 | )
25 | limit: int = 1000
26 | offset: Optional[datetime] = None
27 | direction: TransactionRecordEnumDirection = TransactionRecordEnumDirection.prev
28 |
29 |
30 | class Instrument(BaseModel):
31 | id: UUID4
32 | symbol: str
33 | name: str
34 |
35 |
36 | class Transaction(BaseModel):
37 | account_amount: float
38 | account_balance: float
39 | account_type: str
40 | comment: str
41 | dividend_tax: Optional[dict] = None
42 | dividend: Optional[dict] = None
43 | dnb: bool
44 | fee_base: int
45 | fee_exchange: int
46 | fee_sec: float
47 | fee_taf: float
48 | fee_xtra_shares: int
49 | fill_px: float
50 | fill_qty: float
51 | fin_tran_id: str = Field(alias="finTranID")
52 | fin_tran_type_id: str = Field(alias="finTranTypeID")
53 | instrument: Optional[Instrument] = None
54 | merger_acquisition: Optional[Dict] = None
55 | order_id: Optional[str] = Field(None, alias="orderID")
56 | order_no: Optional[str] = None
57 | position_delta: Optional[float] = None
58 | send_commission_to_inteliclear: bool
59 | symbol: Optional[str] = None
60 | system_amount: int
61 | tran_amount: float
62 | tran_source: str
63 | tran_when: datetime
64 | updated_reason: Optional[str] = None
65 | wlp_amount: int
66 | wlp_fin_tran_type_id: Optional[UUID] = Field(None, alias="wlpFinTranTypeID")
67 | model_config = ConfigDict(alias_generator=camelcase)
68 |
69 |
70 | class TransactionHistoryType(str, enum.Enum):
71 | BUY = "Buy"
72 | CORPORATE_ACTION = "Corporate Action"
73 | DIVIDEND = "Dividend"
74 | DIVIDEND_TAX = "Dividend Tax"
75 | FUNDING = "Funding"
76 | SELL = "Sell"
77 |
78 |
79 | class TransactionsClient(BaseClient):
80 | async def list(self, request: TransactionRecordRequest) -> List[Transaction]:
81 | """Returns the transactions executed by the user.
82 |
83 | Args:
84 | request (TransactionRecordRequest): specify the from+/to datetimes
85 | for the transaction collection.
86 |
87 | Returns:
88 | List[Transaction]: the transactions executed in the time frame.
89 | """
90 | payload = json.loads(request.model_dump_json(by_alias=True))
91 |
92 | data = await self._client.post(
93 | self._client.exchange.account_transactions, payload=payload
94 | )
95 | transactions = []
96 | for d in data:
97 | if d.get("instrument"):
98 | d["symbol"] = d.get("instrument")["symbol"]
99 | transactions.append(Transaction(**d))
100 | return transactions
101 |
--------------------------------------------------------------------------------
/stake/user.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel, ConfigDict
4 | from pydantic.fields import Field
5 |
6 | from stake.common import camelcase
7 |
8 |
9 | class User(BaseModel):
10 | id: str = Field(alias="userId")
11 | first_name: str
12 | last_name: str
13 | email_address: str
14 | mac_status: str
15 | account_type: str
16 | region_identifier: str
17 | dw_account_number: Optional[str] = Field(None, alias="dw_AccountNumber")
18 | can_trade_on_unsettled_funds: Optional[bool] = None
19 | username: Optional[str] = None
20 | model_config = ConfigDict(alias_generator=camelcase, populate_by_name=True)
21 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stabacco/stake-python/6efe9b090748a832352e75ce22a44139bce71101/tests/__init__.py
--------------------------------------------------------------------------------
/tests/cassettes/test_equity/test_list_equities[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/equityPositions
46 | response:
47 | body:
48 | string:
49 | '{"pageNum": 0, "hasNext": false, "equityPositions": [{"instrumentId":
50 | "FUEL.XAU", "symbol": "FUEL", "name": "BETA GLOBAL ENERGY ETF UNITS", "openQty":
51 | "100", "availableForTradingQty": "170", "averagePrice": "5.9400", "marketValue":
52 | "953.7000", "mktPrice": "5.6100", "priorClose": "5.5900", "unrealizedDayPL":
53 | "3.4000", "unrealizedDayPLPercent": 1.0, "unrealizedPL": 1.0, "unrealizedPLPercent":
54 | "-5.56", "recentAnnouncement": false, "sensitive": false}, {"instrumentId":
55 | "WDS.XAU", "symbol": "WDS", "name": "WOODSIDE ENERGY FPO", "openQty": "100",
56 | "availableForTradingQty": "3", "averagePrice": "0", "marketValue": "93.4200",
57 | "mktPrice": "31.1400", "priorClose": "32.5700", "unrealizedDayPL": "-4.2900",
58 | "unrealizedDayPLPercent": 1.0, "unrealizedPL": 1.0, "unrealizedPLPercent":
59 | "0", "recentAnnouncement": true, "sensitive": true}, {"instrumentId": "WTC.XAU",
60 | "symbol": "WTC", "name": "WISETECH GLOBAL LTD FPO", "openQty": "100", "availableForTradingQty":
61 | "10", "averagePrice": "51.5000", "marketValue": "491.8000", "mktPrice": "49.1800",
62 | "priorClose": "47.4900", "unrealizedDayPL": "16.9000", "unrealizedDayPLPercent":
63 | 1.0, "unrealizedPL": 1.0, "unrealizedPLPercent": "-4.50", "recentAnnouncement":
64 | false, "sensitive": false}, {"instrumentId": "ALU.XAU", "symbol": "ALU", "name":
65 | "ALTIUM LIMITED FPO", "openQty": "100", "availableForTradingQty": "20", "averagePrice":
66 | "35.0000", "marketValue": "615.0000", "mktPrice": "30.7500", "priorClose":
67 | "29.6800", "unrealizedDayPL": "21.4000", "unrealizedDayPLPercent": 1.0, "unrealizedPL":
68 | 1.0, "unrealizedPLPercent": "-12.14", "recentAnnouncement": false, "sensitive":
69 | false}, {"instrumentId": "BHP.XAU", "symbol": "BHP", "name": "BHP GROUP LIMITED
70 | FPO", "openQty": "100", "availableForTradingQty": "20", "averagePrice": "50.8000",
71 | "marketValue": "735.8000", "mktPrice": "36.7900", "priorClose": "37.1100",
72 | "unrealizedDayPL": "-6.4000", "unrealizedDayPLPercent": 1.0, "unrealizedPL":
73 | 1.0, "unrealizedPLPercent": "-27.58", "recentAnnouncement": false, "sensitive":
74 | false}, {"instrumentId": "FOOD.XAU", "symbol": "FOOD", "name": "BETA GLOBAL
75 | AGRI ETF UNITS", "openQty": "100", "availableForTradingQty": "100", "averagePrice":
76 | "8.4700", "marketValue": "704.0000", "mktPrice": "7.0400", "priorClose": "7.0000",
77 | "unrealizedDayPL": "4.0000", "unrealizedDayPLPercent": 1.0, "unrealizedPL":
78 | 1.0, "unrealizedPLPercent": "-16.88", "recentAnnouncement": false, "sensitive":
79 | false}]}'
80 | headers: {}
81 | status:
82 | code: 200
83 | message: OK
84 | url: https://global-prd-api.hellostake.com/api/asx/instrument/equityPositions
85 | version: 1
86 |
--------------------------------------------------------------------------------
/tests/cassettes/test_funding/test_cash_available[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/users/accounts/cashAvailableForWithdrawal
46 | response:
47 | body:
48 | string:
49 | '{"cashAvailableForWithdrawal": 1000, "cashAvailableForTrade": 800,
50 | "cashBalance": 1000.0, "reservedCash": 0.0, "liquidCash": 1000.0, "dwCashAvailableForWithdrawal":
51 | 1000, "pendingOrdersAmount": 0.0, "pendingWithdrawals": 0.0, "cardHoldAmount":
52 | 0.0, "pendingPoliAmount": 0.0, "cashSettlement": [{"utcTime": "2022-07-27T13:30:00.001Z",
53 | "cash": 0.0}, {"utcTime": "2022-07-28T13:30:00.001Z", "cash": 0.0}, null]}'
54 | headers: {}
55 | status:
56 | code: 200
57 | message: OK
58 | url: https://global-prd-api.hellostake.com/api/users/accounts/cashAvailableForWithdrawal
59 | version: 1
60 |
--------------------------------------------------------------------------------
/tests/cassettes/test_funding/test_cash_available[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/cash
46 | response:
47 | body:
48 | string:
49 | '{"settledCash": 10000.0, "tradeSettlement": 0.0, "buyingPower": 1000.0,
50 | "pendingBuys": 0.0, "settlementHold": 0.0, "pendingWithdrawals": 0.0, "cashAvailableForWithdrawal":
51 | 1000, "cashAvailableForTransfer": 1000, "cashAvailableForWithdrawalHold":
52 | 0.0, "clearingCash": 0.0}'
53 | headers: {}
54 | status:
55 | code: 200
56 | message: OK
57 | url: https://global-prd-api.hellostake.com/api/asx/cash
58 | version: 1
59 |
--------------------------------------------------------------------------------
/tests/cassettes/test_funding/test_funds_in_flight[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/fund/details
46 | response:
47 | body:
48 | string: '{"fundsInFlight": []}'
49 | headers: {}
50 | status:
51 | code: 200
52 | message: OK
53 | url: https://global-prd-api.hellostake.com/api/fund/details
54 | version: 1
55 |
--------------------------------------------------------------------------------
/tests/cassettes/test_funding/test_funds_in_flight[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/transactions?status=PENDING&status=AWAITING_APPROVAL&size=100&page=0
46 | response:
47 | body:
48 | string: '{"items": [], "hasNext": false, "page": 0, "totalItems": 0}'
49 | headers: {}
50 | status:
51 | code: 200
52 | message: OK
53 | url: https://global-prd-api.hellostake.com/api/asx/transactions?status=PENDING&status=AWAITING_APPROVAL&size=100&page=0
54 | version: 1
55 |
--------------------------------------------------------------------------------
/tests/cassettes/test_funding/test_list_fundings[exchange0-request_0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: POST
45 | uri: https://global-prd-api.hellostake.com/api/users/accounts/transactionHistory
46 | response:
47 | body:
48 | string: '[{"transactionType":
49 | "Funding", "finTranTypeID": "JNLC", "timestamp": "2021-09-30T15:45:50.425Z",
50 | "tranAmount": 1000, "feeAmount": 0.0, "orderID": "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59",
51 | "symbol": null, "side": "CREDIT", "text": "Deposit", "comment": "f1-9011914a",
52 | "amountPerShare": 0.0, "taxRate": 0.0, "reference": "r9-6577610c", "referenceType":
53 | "Funding"},{"transactionType": "Funding", "finTranTypeID": "JNLC", "timestamp":
54 | "2021-08-10T15:45:02.683Z", "tranAmount": 1000, "feeAmount": 0.0, "orderID":
55 | "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59", "symbol": null, "side": "CREDIT",
56 | "text": "Deposit", "comment": "f1-9011914a", "amountPerShare": 0.0, "taxRate":
57 | 0.0, "reference": "r9-6577610c", "referenceType": "Funding"}]'
58 | headers: {}
59 | status:
60 | code: 200
61 | message: OK
62 | url: https://global-prd-api.hellostake.com/api/users/accounts/transactionHistory
63 | - request:
64 | body: null
65 | headers:
66 | Accept:
67 | - application/json
68 | Content-Type:
69 | - application/json
70 | method: GET
71 | uri: https://global-prd-api.hellostake.com/api/users/accounts/transactionDetails?reference=r9-6577610c&referenceType=Funding
72 | response:
73 | body:
74 | string:
75 | '{"insertDate": 1628229668979, "channel": "Poli", "amountTo": 2196.01,
76 | "amountFrom": 3000.0, "status": "RECONCILED", "speed": "Regular", "fxFee":
77 | 20.39, "expressFee": 0.0, "channelFee": null, "totalFee": 20.39, "feesDisplayCurrency":
78 | "USD", "spotRate": 0.7388, "reference": "r9-6577610c", "w8fee": 0.0, "currencyFrom":
79 | "AUD", "currencyTo": "USD", "iof": null, "vet": null, "bsb": null, "accountNumber":
80 | null, "transactionDetails": null}'
81 | headers: {}
82 | status:
83 | code: 200
84 | message: OK
85 | url: https://global-prd-api.hellostake.com/api/users/accounts/transactionDetails?reference=r9-6577610c&referenceType=Funding
86 | - request:
87 | body: null
88 | headers:
89 | Accept:
90 | - application/json
91 | Content-Type:
92 | - application/json
93 | method: GET
94 | uri: https://global-prd-api.hellostake.com/api/users/accounts/transactionDetails?reference=r9-6577610c&referenceType=Funding
95 | response:
96 | body:
97 | string:
98 | '{"insertDate": 1632879790963, "channel": "Poli", "amountTo": 3796.79,
99 | "amountFrom": 5300.0, "status": "RECONCILED", "speed": "Regular", "fxFee":
100 | 35.64, "expressFee": 0.0, "channelFee": null, "totalFee": 35.64, "feesDisplayCurrency":
101 | "USD", "spotRate": 0.7231, "reference": "r9-6577610c", "w8fee": 0.0, "currencyFrom":
102 | "AUD", "currencyTo": "USD", "iof": null, "vet": null, "bsb": null, "accountNumber":
103 | null, "transactionDetails": null}'
104 | headers: {}
105 | status:
106 | code: 200
107 | message: OK
108 | url: https://global-prd-api.hellostake.com/api/users/accounts/transactionDetails?reference=r9-6577610c&referenceType=Funding
109 | version: 1
110 |
--------------------------------------------------------------------------------
/tests/cassettes/test_funding/test_list_fundings[exchange1-request_1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/transactions?status=RECONCILED&size=3&page=0
46 | response:
47 | body:
48 | string:
49 | '{"items": [{"id": "6fd4187a-559b-4326-b9bb-6fb6312ece7b", "side": "CREDIT",
50 | "amount": 18.27, "currency": "AUD", "action": "DIVIDEND_DEPOSIT", "reference":
51 | "r9-6577610c", "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf", "status":
52 | "RECONCILED", "approvedBy": null, "customerFee": 0.0, "insertedAt": "2022-07-18T07:32:28.836634",
53 | "updatedAt": "2022-07-27T07:13:13.455605"}, {"id": "6fd4187a-559b-4326-b9bb-6fb6312ece7b",
54 | "side": "DEBIT", "amount": 1148.28, "currency": "AUD", "action": "SETTLEMENT",
55 | "reference": "r9-6577610c", "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
56 | "status": "RECONCILED", "approvedBy": null, "customerFee": 0.0, "insertedAt":
57 | "2022-07-26T20:02:16.50554", "updatedAt": "2022-07-26T20:02:17.488227"}, {"id":
58 | "6fd4187a-559b-4326-b9bb-6fb6312ece7b", "side": "CREDIT", "amount": 26.58,
59 | "currency": "AUD", "action": "DIVIDEND_DEPOSIT", "reference": "r9-6577610c",
60 | "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf", "status": "RECONCILED",
61 | "approvedBy": null, "customerFee": 0.0, "insertedAt": "2022-01-18T21:24:30.449019",
62 | "updatedAt": "2022-07-14T02:59:09.799755"}], "hasNext": true, "page": 0, "totalItems":
63 | 37}'
64 | headers: {}
65 | status:
66 | code: 200
67 | message: OK
68 | url: https://global-prd-api.hellostake.com/api/asx/transactions?status=RECONCILED&size=3&page=0
69 | version: 1
70 |
--------------------------------------------------------------------------------
/tests/cassettes/test_fx/test_fx_conversion.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "rita19", "emailAddress": "torresbenjamin@gmail.com", "dw_AccountId":
17 | "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber": "w4-1267174s",
18 | "macAccountNumber": "H1-7641957H", "status": null, "macStatus": "BASIC_USER",
19 | "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "lastName": "Alexander", "phoneNumber": "9011530005",
21 | "signUpPhase": 0, "ackSignedWhen": "2021-05-18", "createdDate": 1574303699770,
22 | "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "L7-2127933N", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "mfaenabled": false}'
31 | headers: {}
32 | status:
33 | code: 200
34 | message: OK
35 | url: https://global-prd-api.hellostake.com/api/user
36 | - request:
37 | body: null
38 | headers:
39 | Accept:
40 | - application/json
41 | Content-Type:
42 | - application/json
43 | method: POST
44 | uri: https://global-prd-api.hellostake.com/api/wallet/rate
45 | response:
46 | body:
47 | string:
48 | '{"fromCurrency": "USD", "toCurrency": "AUD", "fromAmount": 1000.0,
49 | "toAmount": 1368.5, "rate": 1.3685, "quote": "7f730dad-86b0-44d7-8810-ed49af26b702"}'
50 | headers: {}
51 | status:
52 | code: 200
53 | message: OK
54 | url: https://global-prd-api.hellostake.com/api/wallet/rate
55 | version: 1
56 |
--------------------------------------------------------------------------------
/tests/cassettes/test_market/test_check_market_status[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/utils/marketStatus
46 | response:
47 | body:
48 | string:
49 | '{"response": {"message": "Market is open Monday through Friday 9:30AM
50 | to 4:00PM EST", "unixtime": "1658228491.36769", "error": "Success", "status":
51 | {"change_at": "08:00:00", "next": "pre", "current": "close"}, "elapsedtime":
52 | "0", "date": "2022-07-19 07:01:31.3676874-04:00", "versionNumber": "2.56.0"}}'
53 | headers: {}
54 | status:
55 | code: 202
56 | message: Accepted
57 | url: https://global-prd-api.hellostake.com/api/utils/marketStatus
58 | version: 1
59 |
--------------------------------------------------------------------------------
/tests/cassettes/test_market/test_check_market_status[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://early-bird-promo.hellostake.com/marketStatus
46 | response:
47 | body:
48 | string:
49 | '{"lastTradingDate": "2022-07-19", "status": {"current": "close"}, "marketLimits":
50 | [[0, 0.005, 0.125], [0.005, 0.02, 0.075], [0.02, 0.05, 0.0625], [0.05, 0.1,
51 | 0.05], [0.1, 0.2, 0.045], [0.2, 0.5, 0.03], [0.5, 1, 0.0125], [1, 2, 0.0125],
52 | [2, 5, 0.01], [5, 15, 0.0075], [15, 39, 0.005], [30, 50, 0.0025], [50, 100,
53 | 0.0025], [100, 250, 0.0025], [250, 500, 0.0025], [500, 9999, 0.0025]], "passiveLimits":
54 | [[0, 0.005, 1], [0.005, 0.02, 0.6], [0.02, 0.05, 0.5], [0.05, 0.1, 0.4], [0.1,
55 | 0.2, 0.4], [0.2, 0.5, 0.4], [0.5, 1, 0.3], [1, 2, 0.3], [2, 5, 0.3], [5, 15,
56 | 0.3], [15, 39, 0.2], [30, 50, 0.2], [50, 100, 0.2], [100, 250, 0.2], [250,
57 | 500, 0.2], [500, 9999, 0.2]]}'
58 | headers: {}
59 | status:
60 | code: 200
61 | message: OK
62 | url: https://early-bird-promo.hellostake.com/marketStatus
63 | version: 1
64 |
--------------------------------------------------------------------------------
/tests/cassettes/test_order/test_brokerage[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/orders/brokerage?orderAmount=1.0
46 | response:
47 | body:
48 | string:
49 | '{"brokerageFee": 3, "fixedFee": 3, "variableFeePercentage": 0.01,
50 | "variableLimit": 30000}'
51 | headers: {}
52 | status:
53 | code: 200
54 | message: OK
55 | url: https://global-prd-api.hellostake.com/api/orders/brokerage?orderAmount=1.0
56 | version: 1
57 |
--------------------------------------------------------------------------------
/tests/cassettes/test_order/test_brokerage[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/orders/brokerage?orderAmount=1.0
46 | response:
47 | body:
48 | string:
49 | '{"brokerageFee": 3.00, "brokerageDiscount": 0.00, "fixedFee": 3.00,
50 | "variableFeePercentage": 0.01, "variableLimit": 30000}'
51 | headers: {}
52 | status:
53 | code: 200
54 | message: OK
55 | url: https://global-prd-api.hellostake.com/api/asx/orders/brokerage?orderAmount=1.0
56 | version: 1
57 |
--------------------------------------------------------------------------------
/tests/cassettes/test_order/test_cancel_order[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/users/accounts/v2/orders
46 | response:
47 | body:
48 | string:
49 | '[{"orderNo": "x5-4334954E", "orderID": "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59",
50 | "orderCashAmt": 100.0, "symbol": "AAPL", "price": 0.0, "stopPrice": 0.0, "side":
51 | "B", "orderType": 1, "cumQty": "0.0", "limitPrice": 0.0, "commission": 0.0,
52 | "createdWhen": "2021-07-04 21:46:25", "orderStatus": 0, "orderQty": 0.6541078,
53 | "description": "Market Order", "instrumentID": "a67422af-8504-43df-9e63-7361eb0bd99e",
54 | "imageUrl": "https://drivewealth.imgix.net/symbols/aapl.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
55 | "instrumentSymbol": "AAPL", "instrumentName": "Apple, Inc.", "encodedName":
56 | "apple-inc-aapl", "expires": "2022-07-27"}]'
57 | headers: {}
58 | status:
59 | code: 200
60 | message: OK
61 | url: https://global-prd-api.hellostake.com/api/users/accounts/v2/orders
62 | - request:
63 | body: null
64 | headers:
65 | Accept:
66 | - application/json
67 | Content-Type:
68 | - application/json
69 | method: DELETE
70 | uri: https://global-prd-api.hellostake.com/api/orders/cancelOrder/HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59
71 | response:
72 | body:
73 | string: ""
74 | headers: {}
75 | status:
76 | code: 200
77 | message: OK
78 | url: https://global-prd-api.hellostake.com/api/orders/cancelOrder/HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59
79 | - request:
80 | body: null
81 | headers:
82 | Accept:
83 | - application/json
84 | Content-Type:
85 | - application/json
86 | method: GET
87 | uri: https://global-prd-api.hellostake.com/api/users/accounts/v2/orders
88 | response:
89 | body:
90 | string: "[]"
91 | headers: {}
92 | status:
93 | code: 200
94 | message: OK
95 | url: https://global-prd-api.hellostake.com/api/users/accounts/v2/orders
96 | version: 1
97 |
--------------------------------------------------------------------------------
/tests/cassettes/test_order/test_list_orders[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/users/accounts/v2/orders
46 | response:
47 | body:
48 | string:
49 | '[{"orderNo": "L4-2572575L", "orderID": "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59",
50 | "orderCashAmt": 0.0, "symbol": "TQQQ", "price": 0.0, "stopPrice": 0.0, "side":
51 | "B", "orderType": 2, "cumQty": "0.0", "limitPrice": 26.7, "commission": 0.0,
52 | "createdWhen": "2020-05-16 04:29:32", "orderStatus": 0, "orderQty": 10.0,
53 | "description": "Limit Order: Buy at a limit of $26.70", "instrumentID": "63698af7-e71e-4424-9ce7-d34b70fbe260",
54 | "imageUrl": "https://drivewealth.imgix.net/symbols/tqqq.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
55 | "instrumentSymbol": "TQQQ", "instrumentName": "ProShares UltraPro QQQ ETF",
56 | "encodedName": "proshares-ultrapro-qqq-etf-tqqq", "expires": "2022-07-21"}]'
57 | headers: {}
58 | status:
59 | code: 200
60 | message: OK
61 | url: https://global-prd-api.hellostake.com/api/users/accounts/v2/orders
62 | version: 1
63 |
--------------------------------------------------------------------------------
/tests/cassettes/test_order/test_list_orders[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/orders
46 | response:
47 | body:
48 | string:
49 | '[{"id": "20ce85f9-2d8d-49ae-8191-39f785c3d5d6", "broker": "FINCLEAR",
50 | "brokerOrderId": null, "brokerOrderVersionId": null, "brokerInstructionId":
51 | 4736823, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
52 | "instrumentId": null, "instrumentCode": "OOO.XAU", "side": "BUY", "limitPrice":
53 | 6.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
54 | "2022-07-21T17:08:38.771843", "completedTimestamp": null, "expiresAt": "2022-08-22T00:00:00",
55 | "orderStatus": "STAKE_PENDING_CREATE", "orderCompletionType": null, "filledUnits":
56 | 0, "averagePrice": null, "unitsRemaining": 221, "estimatedBrokerage": 0.0,
57 | "estimatedExchangeFees": 0.0}]'
58 | headers: {}
59 | status:
60 | code: 200
61 | message: OK
62 | url: https://global-prd-api.hellostake.com/api/asx/orders
63 | version: 1
64 |
--------------------------------------------------------------------------------
/tests/cassettes/test_product/test_find_products_by_name[exchange0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/products/getProductSuggestions/techno
46 | response:
47 | body:
48 | string:
49 | '{"instruments": [{"name": "SKYWATER TECHNOLOGY INC", "symbol": "SKYT",
50 | "encodedName": "skywater-technology-inc-skyt", "instrumentId": "01fdcf5b-6c6d-4928-a875-b4f9212ace3d",
51 | "imageUrl": "https://drivewealth.imgix.net/symbols/dw_default_logo.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
52 | "exchange": null}, {"name": "WARRIOR TECHNOLOGIES ACQ-A", "symbol": "WARR",
53 | "encodedName": "warrior-technologies-acqa-warr", "instrumentId": "03cb7f33-27fb-4ae7-a3f3-ed23be742aa3",
54 | "imageUrl": "https://drivewealth.imgix.net/symbols/WARR.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
55 | "exchange": null}, {"name": "BlackRock Science & Technology Trust II", "symbol":
56 | "BSTZ", "encodedName": "blackrock-science-technology-trust-ii-bstz", "instrumentId":
57 | "04028ea9-6e66-4392-bfef-98a9a48cb9cc", "imageUrl": "https://drivewealth.imgix.net/symbols/bstz_1617916369.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
58 | "exchange": null}, {"name": "Axonics Modulation Technologies Inc", "symbol":
59 | "AXNX", "encodedName": "axonics-modulation-technologies-inc-axnx", "instrumentId":
60 | "05cb4946-46d7-4a49-adab-36d730beaee6", "imageUrl": "https://drivewealth.imgix.net/symbols/axnx.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
61 | "exchange": null}, {"name": "iRhythm Technologies, Inc.", "symbol": "IRTC",
62 | "encodedName": "irhythm-technologies-inc-irtc", "instrumentId": "05f00a67-b822-4023-871a-1d6215a0eef6",
63 | "imageUrl": "https://drivewealth.imgix.net/symbols/irtc.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
64 | "exchange": null}, {"name": "ASPEN TECHNOLOGY INC", "symbol": "AZPN", "encodedName":
65 | "aspen-technology-inc-azpn", "instrumentId": "08b7f76d-fa40-44b0-951e-b77d1a9206e3",
66 | "imageUrl": "https://drivewealth.imgix.net/symbols/azpn_1652797208.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
67 | "exchange": "NSQ"}, {"name": "FARO Technologies Inc.", "symbol": "FARO", "encodedName":
68 | "faro-technologies-inc-faro", "instrumentId": "08c65f62-394e-442a-86c1-0ad93b3efe21",
69 | "imageUrl": "https://drivewealth.imgix.net/symbols/faro.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
70 | "exchange": null}, {"name": "Shift Technologies, Inc.", "symbol": "SFT", "encodedName":
71 | "shift-technologies-inc-sft", "instrumentId": "0951b489-3ca6-41dd-a813-5a97d32043ea",
72 | "imageUrl": "https://drivewealth.imgix.net/symbols/sft_1610660958.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
73 | "exchange": null}, {"name": "Aquabounty Technologies Inc", "symbol": "AQB",
74 | "encodedName": "aquabounty-technologies-inc-aqb", "instrumentId": "0a9ad06a-cbc3-4ae5-86da-8942452908d0",
75 | "imageUrl": "https://drivewealth.imgix.net/symbols/aqb_1616442632.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
76 | "exchange": null}, {"name": "Micron Technology Inc.", "symbol": "MU", "encodedName":
77 | "micron-technology-inc-mu", "instrumentId": "0baafbff-dcc6-4645-8d27-4b4baf8a37ac",
78 | "imageUrl": "https://d3an3cesqmrf1x.cloudfront.net/images/symbols/mu.png",
79 | "exchange": null}], "instrumentTags": [{"tagID": "448f6003-9af8-4f25-8f51-2cf924255f09",
80 | "tagName": "Nanotechnology"}, {"tagID": "55832928-f3cb-4229-9d27-1baa5b25ec15",
81 | "tagName": "Wearable Technology"}, {"tagID": "794e2f64-f33c-4ec7-918a-7ec5c7c998cf",
82 | "tagName": "Technology"}, {"tagID": "e1cdd8d5-c9be-460b-ab56-934907c7d69d",
83 | "tagName": "Biotechnology"}]}'
84 | headers: {}
85 | status:
86 | code: 200
87 | message: OK
88 | url: https://global-prd-api.hellostake.com/api/products/getProductSuggestions/techno
89 | version: 1
90 |
--------------------------------------------------------------------------------
/tests/cassettes/test_product/test_find_products_by_name[exchange1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/search?searchKey=techno
46 | response:
47 | body:
48 | string:
49 | '{"instruments": [{"instrumentId": "AIM.XAU", "symbol": "AIM", "name":
50 | "Ai-Media Technologies Limited", "type": "EQUITY", "recentAnnouncement": false,
51 | "sensitive": false}, {"instrumentId": "CPV.XAU", "symbol": "CPV", "name":
52 | "ClearVue Technologies Limited", "type": "EQUITY", "recentAnnouncement": false,
53 | "sensitive": false}, {"instrumentId": "LCT.XAU", "symbol": "LCT", "name":
54 | "Living Cell Technologies Limited", "type": "EQUITY", "recentAnnouncement":
55 | false, "sensitive": false}, {"instrumentId": "VR1.XAU", "symbol": "VR1", "name":
56 | "Vection Technologies Ltd", "type": "EQUITY", "recentAnnouncement": false,
57 | "sensitive": false}, {"instrumentId": "FFT.XAU", "symbol": "FFT", "name":
58 | "Future First Technologies Ltd", "type": "EQUITY", "recentAnnouncement": false,
59 | "sensitive": false}, {"instrumentId": "VHT.XAU", "symbol": "VHT", "name":
60 | "Volpara Health Technologies Limited", "type": "EQUITY", "recentAnnouncement":
61 | false, "sensitive": false}, {"instrumentId": "VTI.XAU", "symbol": "VTI", "name":
62 | "Visioneering Technologies Inc", "type": "EQUITY", "recentAnnouncement": true,
63 | "sensitive": false}, {"instrumentId": "EGY.XAU", "symbol": "EGY", "name":
64 | "Energy Technologies Limited", "type": "EQUITY", "recentAnnouncement": false,
65 | "sensitive": false}, {"instrumentId": "RTE.XAU", "symbol": "RTE", "name":
66 | "Retech Technology., Co Limited", "type": "EQUITY", "recentAnnouncement":
67 | false, "sensitive": false}, {"instrumentId": "AGI.XAU", "symbol": "AGI", "name":
68 | "Ainsworth Game Technology Limited", "type": "EQUITY", "recentAnnouncement":
69 | true, "sensitive": false}], "instrumentTags": [{"tagName": "Information Technology",
70 | "tagId": "45"}, {"tagName": "Pharmaceuticals, Biotechnology & Life Sciences",
71 | "tagId": "3520"}, {"tagName": "Technology Hardware & Equipment", "tagId":
72 | "4520"}, {"tagName": "Health Care Technology", "tagId": "351030"}, {"tagName":
73 | "Biotechnology", "tagId": "352010"}, {"tagName": "Technology Hardware, Storage
74 | & Peripherals", "tagId": "452020"}, {"tagName": "Health Care Technology",
75 | "tagId": "35103010"}, {"tagName": "Biotechnology", "tagId": "35201010"}, {"tagName":
76 | "Technology Hardware, Storage & Peripherals", "tagId": "45202030"}, {"tagName":
77 | "Technology Distributors", "tagId": "45203030"}]}'
78 | headers: {}
79 | status:
80 | code: 200
81 | message: OK
82 | url: https://global-prd-api.hellostake.com/api/asx/instrument/search?searchKey=techno
83 | version: 1
84 |
--------------------------------------------------------------------------------
/tests/cassettes/test_product/test_get_product[exchange1-symbols1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "leonardzachary", "emailAddress": "ellen70@bates-williams.com",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "R6-1813041A", "macAccountNumber": "K4-3517949y", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "middleName": null, "lastName": "Alexander", "phoneNumber":
21 | "9011530005", "signUpPhase": 0, "ackSignedWhen": "2022-01-22", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "A1-2107594j", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/ANZ
46 | response:
47 | body:
48 | string:
49 | '{"marketStatus": "CLOSED", "lastTradedExchange": "ASX", "symbol": "ANZ",
50 | "lastTradedTimestamp": 1658383848, "lastTrade": "21.9300", "bid": "21.9200",
51 | "ask": "21.9300", "priorClose": "21.6400", "open": "22.3200", "high": "22.3400",
52 | "low": "21.2700", "pointsChange": "0.2900", "percentageChange": "1.34", "outOfMarketPrice":
53 | "21.9300", "outOfMarketQuantity": "3393713", "outOfMarketSurplus": "-69162"}'
54 | headers: {}
55 | status:
56 | code: 200
57 | message: OK
58 | url: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/ANZ
59 | - request:
60 | body: null
61 | headers:
62 | Accept:
63 | - application/json
64 | Content-Type:
65 | - application/json
66 | method: GET
67 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/WDS
68 | response:
69 | body:
70 | string:
71 | '{"marketStatus": "CLOSED", "lastTradedExchange": "ASX", "symbol": "WDS",
72 | "lastTradedTimestamp": 1658383848, "lastTrade": "31.1400", "bid": "30.9600",
73 | "ask": "30.9900", "priorClose": "32.5700", "open": "31.8400", "high": "32.1400",
74 | "low": "30.7000", "pointsChange": "-1.5800", "percentageChange": "-4.39",
75 | "outOfMarketPrice": "31.1400", "outOfMarketQuantity": "2070129", "outOfMarketSurplus":
76 | "-16665"}'
77 | headers: {}
78 | status:
79 | code: 200
80 | message: OK
81 | url: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/WDS
82 | - request:
83 | body: null
84 | headers:
85 | Accept:
86 | - application/json
87 | Content-Type:
88 | - application/json
89 | method: GET
90 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/COL
91 | response:
92 | body:
93 | string:
94 | '{"marketStatus": "CLOSED", "lastTradedExchange": "ASX", "symbol": "COL",
95 | "lastTradedTimestamp": 1658383850, "lastTrade": "18.8800", "bid": "18.8800",
96 | "ask": "18.8900", "priorClose": "18.6300", "open": "18.7900", "high": "18.9700",
97 | "low": "18.6400", "pointsChange": "0.2500", "percentageChange": "1.34", "outOfMarketPrice":
98 | "18.8800", "outOfMarketQuantity": "1076585", "outOfMarketSurplus": "42836"}'
99 | headers: {}
100 | status:
101 | code: 200
102 | message: OK
103 | url: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/COL
104 | version: 1
105 |
--------------------------------------------------------------------------------
/tests/cassettes/test_ratings/test_list_ratings.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "rita19", "emailAddress": "torresbenjamin@gmail.com", "dw_AccountId":
17 | "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber": "w4-1267174s",
18 | "macAccountNumber": "H1-7641957H", "status": null, "macStatus": "BASIC_USER",
19 | "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "lastName": "Alexander", "phoneNumber": "9011530005",
21 | "signUpPhase": 0, "ackSignedWhen": "2021-05-18", "createdDate": 1574303699770,
22 | "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "L7-2127933N", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "mfaenabled": false}'
31 | headers: {}
32 | status:
33 | code: 200
34 | message: OK
35 | url: https://global-prd-api.hellostake.com/api/user
36 | - request:
37 | body: null
38 | headers:
39 | Accept:
40 | - application/json
41 | Content-Type:
42 | - application/json
43 | method: GET
44 | uri: https://global-prd-api.hellostake.com/api/data/calendar/ratings?tickers=AAPL,MSFT&pageSize=4
45 | response:
46 | body:
47 | string:
48 | '{"ratings": [{"id": "a154926a-4540-437c-aef4-c0de3671cfe6", "date":
49 | "2022-01-14", "time": "07:04:52", "ticker": "AAPL", "exchange": "NASDAQ",
50 | "name": "Apple", "analyst": "Loop Capital", "currency": "USD", "url": "https://www.benzinga.com/stock/AAPL/ratings",
51 | "importance": 0, "notes": "", "updated": 1642161976, "action_pt": "Raises",
52 | "action_company": "Maintains", "rating_current": "Buy", "pt_current": "210.0000",
53 | "rating_prior": "", "pt_prior": "165.0000", "url_calendar": "https://www.benzinga.com/stock/AAPL/ratings",
54 | "url_news": "https://www.benzinga.com/stock-articles/AAPL/analyst-ratings",
55 | "analyst_name": "Ananda Baruah"}, {"id": "a154926a-4540-437c-aef4-c0de3671cfe6",
56 | "date": "2021-12-22", "time": "09:51:07", "ticker": "AAPL", "exchange": "NASDAQ",
57 | "name": "Apple", "analyst": "Citigroup", "currency": "USD", "url": "https://www.benzinga.com/stock/AAPL/ratings",
58 | "importance": 0, "notes": "", "updated": 1640184748, "action_pt": "Raises",
59 | "action_company": "Maintains", "rating_current": "Buy", "pt_current": "200.0000",
60 | "rating_prior": "", "pt_prior": "170.0000", "url_calendar": "https://www.benzinga.com/stock/AAPL/ratings",
61 | "url_news": "https://www.benzinga.com/stock-articles/AAPL/analyst-ratings",
62 | "analyst_name": "Jim Suva"}, {"id": "a154926a-4540-437c-aef4-c0de3671cfe6",
63 | "date": "2021-12-22", "time": "05:01:24", "ticker": "MSFT", "exchange": "NASDAQ",
64 | "name": "Microsoft", "analyst": "SMBC Nikko", "currency": "USD", "url": "https://www.benzinga.com/stock/MSFT/ratings",
65 | "importance": 0, "notes": "", "updated": 1640167354, "action_pt": "Announces",
66 | "action_company": "Initiates Coverage On", "rating_current": "Outperform",
67 | "pt_current": "410.0000", "rating_prior": "", "pt_prior": "", "url_calendar":
68 | "https://www.benzinga.com/stock/MSFT/ratings", "url_news": "https://www.benzinga.com/stock-articles/MSFT/analyst-ratings",
69 | "analyst_name": "Steve Koenig"}, {"id": "a154926a-4540-437c-aef4-c0de3671cfe6",
70 | "date": "2021-12-14", "time": "08:12:14", "ticker": "AAPL", "exchange": "NASDAQ",
71 | "name": "Apple", "analyst": "Evercore ISI Group", "currency": "USD", "url":
72 | "https://www.benzinga.com/stock/AAPL/ratings", "importance": 0, "notes": "",
73 | "updated": 1639487616, "action_pt": "Raises", "action_company": "Maintains",
74 | "rating_current": "Outperform", "pt_current": "200.0000", "rating_prior":
75 | "", "pt_prior": "180.0000", "url_calendar": "https://www.benzinga.com/stock/AAPL/ratings",
76 | "url_news": "https://www.benzinga.com/stock-articles/AAPL/analyst-ratings",
77 | "analyst_name": "Amit Daryanani"}]}'
78 | headers: {}
79 | status:
80 | code: 200
81 | message: OK
82 | url: https://global-prd-api.hellostake.com/api/data/calendar/ratings?tickers=AAPL,MSFT&pageSize=4
83 | version: 1
84 |
--------------------------------------------------------------------------------
/tests/cassettes/test_ratings/test_list_ratings_unknown.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/data/calendar/ratings?tickers=NOTEXIST&pageSize=4
46 | response:
47 | body:
48 | string: '{"message": "No data returned"}'
49 | headers: {}
50 | status:
51 | code: 200
52 | message: OK
53 | url: https://global-prd-api.hellostake.com/api/data/calendar/ratings?tickers=NOTEXIST&pageSize=4
54 | version: 1
55 |
--------------------------------------------------------------------------------
/tests/cassettes/test_trade/test_sell[exchange0-request_0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/COL
46 | response:
47 | body:
48 | string:
49 | '{"marketStatus": "CLOSED", "lastTradedExchange": "ASX", "symbol": "COL",
50 | "lastTradedTimestamp": 1658988616, "lastTrade": "18.6400", "bid": "18.6300",
51 | "ask": "18.6400", "priorClose": "18.7100", "open": "18.6900", "high": "18.6900",
52 | "low": "18.4900", "pointsChange": "-0.0700", "percentageChange": "-0.37",
53 | "outOfMarketPrice": "18.6400", "outOfMarketQuantity": "848925", "outOfMarketSurplus":
54 | "-56140"}'
55 | headers: {}
56 | status:
57 | code: 200
58 | message: OK
59 | url: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/COL
60 | - request:
61 | body: null
62 | headers:
63 | Accept:
64 | - application/json
65 | Content-Type:
66 | - application/json
67 | method: POST
68 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
69 | response:
70 | body:
71 | string: '{"instrumentId": "COL.XAU"}'
72 | headers: {}
73 | status:
74 | code: 201
75 | message: Created
76 | url: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
77 | - request:
78 | body: null
79 | headers:
80 | Accept:
81 | - application/json
82 | Content-Type:
83 | - application/json
84 | method: POST
85 | uri: https://global-prd-api.hellostake.com/api/asx/orders
86 | response:
87 | body:
88 | string:
89 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
90 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
91 | 4841281, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
92 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "SELL", "limitPrice":
93 | 18.63, "validity": "GTC", "validityDate": null, "type": "MARKET_TO_LIMIT",
94 | "placedTimestamp": "2022-07-28T20:49:39.621702", "completedTimestamp": null,
95 | "expiresAt": "2022-08-29T00:00:00", "orderStatus": "STAKE_PENDING_CREATE",
96 | "orderCompletionType": null, "filledUnits": 0, "averagePrice": null, "unitsRemaining":
97 | 20, "estimatedBrokerage": 0.0, "estimatedExchangeFees": 0.0}}'
98 | headers: {}
99 | status:
100 | code: 200
101 | message: OK
102 | url: https://global-prd-api.hellostake.com/api/asx/orders
103 | - request:
104 | body: null
105 | headers:
106 | Accept:
107 | - application/json
108 | Content-Type:
109 | - application/json
110 | method: GET
111 | uri: https://global-prd-api.hellostake.com/api/asx/orders
112 | response:
113 | body:
114 | string:
115 | '[{"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker": "FINCLEAR",
116 | "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
117 | 4841281, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
118 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "SELL", "limitPrice":
119 | 18.63, "validity": "GTC", "validityDate": null, "type": "MARKET_TO_LIMIT",
120 | "placedTimestamp": "2022-07-28T20:49:39.621702", "completedTimestamp": null,
121 | "expiresAt": "2022-08-29T00:00:00", "orderStatus": "STAKE_PENDING_CREATE",
122 | "orderCompletionType": null, "filledUnits": 0, "averagePrice": null, "unitsRemaining":
123 | 20, "estimatedBrokerage": 0.0, "estimatedExchangeFees": 0.0}]'
124 | headers: {}
125 | status:
126 | code: 200
127 | message: OK
128 | url: https://global-prd-api.hellostake.com/api/asx/orders
129 | - request:
130 | body: null
131 | headers:
132 | Accept:
133 | - application/json
134 | Content-Type:
135 | - application/json
136 | method: POST
137 | uri: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
138 | response:
139 | body:
140 | string:
141 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
142 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
143 | 4841281, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
144 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "SELL", "limitPrice":
145 | 18.63, "validity": "GTC", "validityDate": null, "type": "MARKET_TO_LIMIT",
146 | "placedTimestamp": "2022-07-28T20:49:39.621702", "completedTimestamp": null,
147 | "expiresAt": "2022-08-29T00:00:00", "orderStatus": "CLOSED", "orderCompletionType":
148 | "CANCELLED", "filledUnits": 0, "averagePrice": null, "unitsRemaining": 20,
149 | "estimatedBrokerage": 0.0, "estimatedExchangeFees": 0.0}}'
150 | headers: {}
151 | status:
152 | code: 200
153 | message: OK
154 | url: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
155 | version: 1
156 |
--------------------------------------------------------------------------------
/tests/cassettes/test_trade/test_sell[exchange1-request_1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: POST
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
46 | response:
47 | body:
48 | string: '{"instrumentId": "COL.XAU"}'
49 | headers: {}
50 | status:
51 | code: 201
52 | message: Created
53 | url: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
54 | - request:
55 | body: null
56 | headers:
57 | Accept:
58 | - application/json
59 | Content-Type:
60 | - application/json
61 | method: POST
62 | uri: https://global-prd-api.hellostake.com/api/asx/orders
63 | response:
64 | body:
65 | string:
66 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
67 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
68 | 4841283, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
69 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "SELL", "limitPrice":
70 | 15.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
71 | "2022-07-28T20:49:40.810211", "completedTimestamp": null, "expiresAt": "2022-08-29T00:00:00",
72 | "orderStatus": "STAKE_PENDING_CREATE", "orderCompletionType": null, "filledUnits":
73 | 0, "averagePrice": null, "unitsRemaining": 20, "estimatedBrokerage": 0.0,
74 | "estimatedExchangeFees": 0.0}}'
75 | headers: {}
76 | status:
77 | code: 200
78 | message: OK
79 | url: https://global-prd-api.hellostake.com/api/asx/orders
80 | - request:
81 | body: null
82 | headers:
83 | Accept:
84 | - application/json
85 | Content-Type:
86 | - application/json
87 | method: GET
88 | uri: https://global-prd-api.hellostake.com/api/asx/orders
89 | response:
90 | body:
91 | string:
92 | '[{"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker": "FINCLEAR",
93 | "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
94 | 4841283, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
95 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "SELL", "limitPrice":
96 | 15.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
97 | "2022-07-28T20:49:40.810211", "completedTimestamp": null, "expiresAt": "2022-08-29T00:00:00",
98 | "orderStatus": "STAKE_PENDING_CREATE", "orderCompletionType": null, "filledUnits":
99 | 0, "averagePrice": null, "unitsRemaining": 20, "estimatedBrokerage": 0.0,
100 | "estimatedExchangeFees": 0.0}]'
101 | headers: {}
102 | status:
103 | code: 200
104 | message: OK
105 | url: https://global-prd-api.hellostake.com/api/asx/orders
106 | - request:
107 | body: null
108 | headers:
109 | Accept:
110 | - application/json
111 | Content-Type:
112 | - application/json
113 | method: POST
114 | uri: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
115 | response:
116 | body:
117 | string:
118 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
119 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
120 | 4841283, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
121 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "SELL", "limitPrice":
122 | 15.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
123 | "2022-07-28T20:49:40.810211", "completedTimestamp": null, "expiresAt": "2022-08-29T00:00:00",
124 | "orderStatus": "CLOSED", "orderCompletionType": "CANCELLED", "filledUnits":
125 | 0, "averagePrice": null, "unitsRemaining": 20, "estimatedBrokerage": 0.0,
126 | "estimatedExchangeFees": 0.0}}'
127 | headers: {}
128 | status:
129 | code: 200
130 | message: OK
131 | url: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
132 | version: 1
133 |
--------------------------------------------------------------------------------
/tests/cassettes/test_trade/test_successful_trade[exchange1-request_1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/COL
46 | response:
47 | body:
48 | string:
49 | '{"marketStatus": "CLOSED", "lastTradedExchange": "ASX", "symbol": "COL",
50 | "lastTradedTimestamp": 1658902258, "lastTrade": "18.7100", "bid": "18.7100",
51 | "ask": "18.7200", "priorClose": "18.7800", "open": "18.8900", "high": "18.9400",
52 | "low": "18.6400", "pointsChange": "-0.0700", "percentageChange": "-0.37",
53 | "outOfMarketPrice": "18.7100", "outOfMarketQuantity": "652055", "outOfMarketSurplus":
54 | "38105"}'
55 | headers: {}
56 | status:
57 | code: 200
58 | message: OK
59 | url: https://global-prd-api.hellostake.com/api/asx/instrument/singleQuote/COL
60 | - request:
61 | body: null
62 | headers:
63 | Accept:
64 | - application/json
65 | Content-Type:
66 | - application/json
67 | method: POST
68 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
69 | response:
70 | body:
71 | string: '{"instrumentId": "COL.XAU"}'
72 | headers: {}
73 | status:
74 | code: 201
75 | message: Created
76 | url: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
77 | - request:
78 | body: null
79 | headers:
80 | Accept:
81 | - application/json
82 | Content-Type:
83 | - application/json
84 | method: POST
85 | uri: https://global-prd-api.hellostake.com/api/asx/orders
86 | response:
87 | body:
88 | string:
89 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
90 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
91 | 4816685, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
92 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "BUY", "limitPrice":
93 | 18.72, "validity": "GTC", "validityDate": null, "type": "MARKET_TO_LIMIT",
94 | "placedTimestamp": "2022-07-27T22:22:45.164059", "completedTimestamp": null,
95 | "expiresAt": "2022-08-26T00:00:00", "orderStatus": "STAKE_PENDING_CREATE",
96 | "orderCompletionType": null, "filledUnits": 0, "averagePrice": null, "unitsRemaining":
97 | 20, "estimatedBrokerage": 0.0, "estimatedExchangeFees": 0.0}}'
98 | headers: {}
99 | status:
100 | code: 200
101 | message: OK
102 | url: https://global-prd-api.hellostake.com/api/asx/orders
103 | - request:
104 | body: null
105 | headers:
106 | Accept:
107 | - application/json
108 | Content-Type:
109 | - application/json
110 | method: GET
111 | uri: https://global-prd-api.hellostake.com/api/asx/orders
112 | response:
113 | body:
114 | string:
115 | '[{"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker": "FINCLEAR",
116 | "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
117 | 4816685, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
118 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "BUY", "limitPrice":
119 | 18.72, "validity": "GTC", "validityDate": null, "type": "MARKET_TO_LIMIT",
120 | "placedTimestamp": "2022-07-27T22:22:45.164059", "completedTimestamp": null,
121 | "expiresAt": "2022-08-26T00:00:00", "orderStatus": "STAKE_PENDING_CREATE",
122 | "orderCompletionType": null, "filledUnits": 0, "averagePrice": null, "unitsRemaining":
123 | 20, "estimatedBrokerage": 0.0, "estimatedExchangeFees": 0.0}]'
124 | headers: {}
125 | status:
126 | code: 200
127 | message: OK
128 | url: https://global-prd-api.hellostake.com/api/asx/orders
129 | - request:
130 | body: null
131 | headers:
132 | Accept:
133 | - application/json
134 | Content-Type:
135 | - application/json
136 | method: POST
137 | uri: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
138 | response:
139 | body:
140 | string:
141 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
142 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
143 | 4816685, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
144 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "BUY", "limitPrice":
145 | 18.72, "validity": "GTC", "validityDate": null, "type": "MARKET_TO_LIMIT",
146 | "placedTimestamp": "2022-07-27T22:22:45.164059", "completedTimestamp": null,
147 | "expiresAt": "2022-08-26T00:00:00", "orderStatus": "CLOSED", "orderCompletionType":
148 | "CANCELLED", "filledUnits": 0, "averagePrice": null, "unitsRemaining": 20,
149 | "estimatedBrokerage": 0.0, "estimatedExchangeFees": 0.0}}'
150 | headers: {}
151 | status:
152 | code: 200
153 | message: OK
154 | url: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
155 | version: 1
156 |
--------------------------------------------------------------------------------
/tests/cassettes/test_trade/test_successful_trade[exchange3-request_3].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: POST
45 | uri: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
46 | response:
47 | body:
48 | string: '{"instrumentId": "COL.XAU"}'
49 | headers: {}
50 | status:
51 | code: 201
52 | message: Created
53 | url: https://global-prd-api.hellostake.com/api/asx/instrument/view/COL
54 | - request:
55 | body: null
56 | headers:
57 | Accept:
58 | - application/json
59 | Content-Type:
60 | - application/json
61 | method: POST
62 | uri: https://global-prd-api.hellostake.com/api/asx/orders
63 | response:
64 | body:
65 | string:
66 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
67 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
68 | 4816686, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
69 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "BUY", "limitPrice":
70 | 12.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
71 | "2022-07-27T22:22:49.881946", "completedTimestamp": null, "expiresAt": "2022-08-26T00:00:00",
72 | "orderStatus": "STAKE_PENDING_CREATE", "orderCompletionType": null, "filledUnits":
73 | 0, "averagePrice": null, "unitsRemaining": 20, "estimatedBrokerage": 0.0,
74 | "estimatedExchangeFees": 0.0}}'
75 | headers: {}
76 | status:
77 | code: 200
78 | message: OK
79 | url: https://global-prd-api.hellostake.com/api/asx/orders
80 | - request:
81 | body: null
82 | headers:
83 | Accept:
84 | - application/json
85 | Content-Type:
86 | - application/json
87 | method: GET
88 | uri: https://global-prd-api.hellostake.com/api/asx/orders
89 | response:
90 | body:
91 | string:
92 | '[{"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker": "FINCLEAR",
93 | "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
94 | 4816686, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
95 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "BUY", "limitPrice":
96 | 12.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
97 | "2022-07-27T22:22:49.881946", "completedTimestamp": null, "expiresAt": "2022-08-26T00:00:00",
98 | "orderStatus": "STAKE_PENDING_CREATE", "orderCompletionType": null, "filledUnits":
99 | 0, "averagePrice": null, "unitsRemaining": 20, "estimatedBrokerage": 0.0,
100 | "estimatedExchangeFees": 0.0}]'
101 | headers: {}
102 | status:
103 | code: 200
104 | message: OK
105 | url: https://global-prd-api.hellostake.com/api/asx/orders
106 | - request:
107 | body: null
108 | headers:
109 | Accept:
110 | - application/json
111 | Content-Type:
112 | - application/json
113 | method: POST
114 | uri: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
115 | response:
116 | body:
117 | string:
118 | '{"order": {"id": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "broker":
119 | "FINCLEAR", "brokerOrderId": 11111, "brokerOrderVersionId": null, "brokerInstructionId":
120 | 4816686, "brokerInstructionVersionId": 2, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
121 | "instrumentId": null, "instrumentCode": "COL.XAU", "side": "BUY", "limitPrice":
122 | 12.0, "validity": "GTC", "validityDate": null, "type": "LIMIT", "placedTimestamp":
123 | "2022-07-27T22:22:49.881946", "completedTimestamp": null, "expiresAt": "2022-08-26T00:00:00",
124 | "orderStatus": "CLOSED", "orderCompletionType": "CANCELLED", "filledUnits":
125 | 0, "averagePrice": null, "unitsRemaining": 20, "estimatedBrokerage": 0.0,
126 | "estimatedExchangeFees": 0.0}}'
127 | headers: {}
128 | status:
129 | code: 200
130 | message: OK
131 | url: https://global-prd-api.hellostake.com/api/asx/orders/1cf93550-8eb4-4c32-a229-826cf8c1be59/cancel
132 | version: 1
133 |
--------------------------------------------------------------------------------
/tests/cassettes/test_transaction/test_list_transactions[exchange0-request_0].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: POST
45 | uri: https://global-prd-api.hellostake.com/api/users/accounts/accountTransactions
46 | response:
47 | body:
48 | string:
49 | '[{"accountAmount": 0.62, "accountBalance": 29442.32, "accountType":
50 | "LIVE", "comment": "f1-9011914a", "dnb": false, "finTranID": "1cf93550-8eb4-4c32-a229-826cf8c1be59",
51 | "finTranTypeID": "DIV", "feeSec": 0.0, "feeTaf": 0.0, "feeBase": 0.0, "feeXtraShares":
52 | 0.0, "feeExchange": 0.0, "fillQty": 0.0, "fillPx": 0.0, "sendCommissionToInteliclear":
53 | false, "systemAmount": 0.0, "tranAmount": 1000, "tranSource": "INTE", "tranWhen":
54 | "2020-06-09 03:35:08", "wlpAmount": 0.0, "wlpFinTranTypeID": "b553ef2c-13f8-411b-977c-00fbc5efbe59",
55 | "instrument": {"id": "20076f7d-ff21-4741-86be-33dac978ceff", "symbol": "LIT",
56 | "name": "Global X Lithium & Battery Tech ETF"}, "dividend": {"type": "CASH",
57 | "amountPerShare": 0.0622, "taxCode": "FULLY_TAXABLE"}, "dividendTax": null,
58 | "mergerAcquisition": null, "positionDelta": null, "orderID": "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59",
59 | "orderNo": "x5-4334954E"}, {"accountAmount": 3.5, "accountBalance": 29445.82,
60 | "accountType": "LIVE", "comment": "f1-9011914a", "dnb": false, "finTranID":
61 | "1cf93550-8eb4-4c32-a229-826cf8c1be59", "finTranTypeID": "DIV", "feeSec":
62 | 0.0, "feeTaf": 0.0, "feeBase": 0.0, "feeXtraShares": 0.0, "feeExchange": 0.0,
63 | "fillQty": 0.0, "fillPx": 0.0, "sendCommissionToInteliclear": false, "systemAmount":
64 | 0.0, "tranAmount": 1000, "tranSource": "INTE", "tranWhen": "2020-06-09 03:35:08",
65 | "wlpAmount": 0.0, "wlpFinTranTypeID": "b553ef2c-13f8-411b-977c-00fbc5efbe59",
66 | "instrument": {"id": "20076f7d-ff21-4741-86be-33dac978ceff", "symbol": "COP",
67 | "name": "ConocoPhillips"}, "dividend": {"type": "CASH", "amountPerShare":
68 | 0.7, "taxCode": "FULLY_TAXABLE"}, "dividendTax": null, "mergerAcquisition":
69 | null, "positionDelta": null, "orderID": "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59",
70 | "orderNo": "x5-4334954E"}, {"accountAmount": -0.53, "accountBalance": 29445.29,
71 | "accountType": "LIVE", "comment": "f1-9011914a", "dnb": false, "finTranID":
72 | "1cf93550-8eb4-4c32-a229-826cf8c1be59", "finTranTypeID": "DIVTAX", "feeSec":
73 | 0.0, "feeTaf": 0.0, "feeBase": 0.0, "feeXtraShares": 0.0, "feeExchange": 0.0,
74 | "fillQty": 0.0, "fillPx": 0.0, "sendCommissionToInteliclear": false, "systemAmount":
75 | 0.0, "tranAmount": 1000, "tranSource": "INTE", "tranWhen": "2020-06-09 03:35:08",
76 | "wlpAmount": 0.0, "wlpFinTranTypeID": "87f1acf2-b9ea-4781-b989-e403459626bb",
77 | "instrument": {"id": "20076f7d-ff21-4741-86be-33dac978ceff", "symbol": "COP",
78 | "name": "ConocoPhillips"}, "dividend": null, "dividendTax": {"rate": 0.15,
79 | "type": "NON_RESIDENT_ALIEN"}, "mergerAcquisition": null, "positionDelta":
80 | null, "orderID": "HHI.1cf93550-8eb4-4c32-a229-826cf8c1be59", "orderNo": "x5-4334954E"}]'
81 | headers: {}
82 | status:
83 | code: 200
84 | message: OK
85 | url: https://global-prd-api.hellostake.com/api/users/accounts/accountTransactions
86 | version: 1
87 |
--------------------------------------------------------------------------------
/tests/cassettes/test_transaction/test_list_transactions[exchange1-request_1].yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "michael29", "emailAddress": "reevesmegan@gilmore-wright.biz",
17 | "dw_AccountId": "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber":
18 | "z0-0593879b", "macAccountNumber": "d9-0481457G", "status": null, "macStatus":
19 | "BASIC_USER", "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Rita", "middleName": null, "lastName": "Jones", "phoneNumber":
21 | "(640)242-4270x965", "signUpPhase": 0, "ackSignedWhen": "2021-11-15", "createdDate":
22 | 1574303699770, "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "W2-6612029X", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "accountStatus": "OPEN", "mfaenabled":
31 | false}'
32 | headers: {}
33 | status:
34 | code: 200
35 | message: OK
36 | url: https://global-prd-api.hellostake.com/api/user
37 | - request:
38 | body: null
39 | headers:
40 | Accept:
41 | - application/json
42 | Content-Type:
43 | - application/json
44 | method: GET
45 | uri: https://global-prd-api.hellostake.com/api/asx/orders/tradeActivity?size=3&page=0
46 | response:
47 | body:
48 | string:
49 | '{"items": [{"brokerOrderId": 11111, "instrumentCode": "CBA.XAU", "type":
50 | "MARKET_TO_LIMIT", "side": "BUY", "limitPrice": 97.3, "averagePrice": 97.08,
51 | "completedTimestamp": "2022-07-25T05:17:10.595", "placedTimestamp": "2022-07-25T15:17:10.231591",
52 | "orderStatus": "CLOSED", "orderCompletionType": "FILLED", "consideration":
53 | 582.48, "effectivePrice": 97.08, "units": 6, "userBrokerageFees": 0.0, "executionDate":
54 | "2022-07-25", "contractNoteReceived": true, "contractNoteNumber": "1799186",
55 | "contractNoteNumbers": ["1799186"]}, {"brokerOrderId": 11111, "instrumentCode":
56 | "COL.XAU", "type": "MARKET_TO_LIMIT", "side": "BUY", "limitPrice": 18.95,
57 | "averagePrice": 18.86, "completedTimestamp": "2022-07-25T05:04:26.126", "placedTimestamp":
58 | "2022-07-25T15:04:25.685884", "oexerderStatus": "CLOSED", "orderCompletionType":
59 | "FILLED", "consideration": 565.8, "effectivePrice": 18.86, "units": 30, "userBrokerageFees":
60 | 0.0, "executionDate": "2022-07-25", "contractNoteReceived": true, "contractNoteNumber":
61 | "1799124", "contractNoteNumbers": ["1799124"]}, {"brokerOrderId": 11111, "instrumentCode":
62 | "VMM.XAU", "type": "MARKET_TO_LIMIT", "side": "SELL", "limitPrice": 0.295,
63 | "averagePrice": 0.295, "completedTimestamp": "2022-05-20T05:49:08.817", "placedTimestamp":
64 | "2022-05-20T15:46:46.189472", "orderStatus": "CLOSED", "orderCompletionType":
65 | "FILLED", "consideration": 590.0, "effectivePrice": 0.295, "units": 2000,
66 | "userBrokerageFees": 0.0, "executionDate": "2022-05-20", "contractNoteReceived":
67 | true, "contractNoteNumber": "1575807", "contractNoteNumbers": ["1575807"]}],
68 | "hasNext": true, "page": 0, "totalItems": 38}'
69 | headers: {}
70 | status:
71 | code: 200
72 | message: OK
73 | url: https://global-prd-api.hellostake.com/api/asx/orders/tradeActivity?size=3&page=0
74 | version: 1
75 |
--------------------------------------------------------------------------------
/tests/cassettes/test_watchlist/test_add_to_watchlist.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "rita19", "emailAddress": "torresbenjamin@gmail.com", "dw_AccountId":
17 | "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber": "w4-1267174s",
18 | "macAccountNumber": "H1-7641957H", "status": null, "macStatus": "BASIC_USER",
19 | "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "lastName": "Alexander", "phoneNumber": "9011530005",
21 | "signUpPhase": 0, "ackSignedWhen": "2021-05-18", "createdDate": 1574303699770,
22 | "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "L7-2127933N", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "mfaenabled": false}'
31 | headers: {}
32 | status:
33 | code: 200
34 | message: OK
35 | url: https://global-prd-api.hellostake.com/api/user
36 | - request:
37 | body: null
38 | headers:
39 | Accept:
40 | - application/json
41 | Content-Type:
42 | - application/json
43 | method: GET
44 | uri: https://global-prd-api.hellostake.com/api/products/searchProduct?symbol=SPOT&page=1&max=1
45 | response:
46 | body:
47 | string:
48 | '{"products": [{"id": "b738ff53-b576-4f09-822e-91128def0560", "instrumentTypeID":
49 | null, "symbol": "SPOT", "description": "Spotify Technology SA a Luxembourg-based
50 | company, which offers digital music-streaming services. The Company enables
51 | users to discover new releases, which includes the latest singles and albums;
52 | playlists, which includes ready-made playlists put together by music fans
53 | and experts, and over millions of songs so that users can play their favorites,
54 | discover new tracks and build a personalized collection. Users can either
55 | select Spotify Free, which includes only shuffle play or Spotify Premium,
56 | which encompasses a range of features, such as shuffle play, advertisement
57 | free, unlimited skips, listen offline, play any track and high quality audio.
58 | The Company operates through a number of subsidiaries, including Spotify LTD
59 | and is present in over 20 countries.", "category": "Stock", "currencyID":
60 | null, "urlImage": "https://drivewealth.imgix.net/symbols/spot.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
61 | "sector": null, "name": "Spotify Technology SA", "dailyReturn": -3.32, "dailyReturnPercentage":
62 | -1.57, "lastTraded": 207.55, "monthlyReturn": 0.0, "yearlyReturnPercentage":
63 | 92.8, "yearlyReturnValue": 131.68, "popularity": 9470.0, "watched": 8075,
64 | "news": 0, "bought": 9479, "viewed": 23400, "productType": "Instrument", "exchange":
65 | null, "status": "ACTIVE", "type": "EQUITY", "encodedName": "spotify-technology-sa-spot",
66 | "period": "YEAR RETURN", "inceptionDate": 1522713600000, "instrumentTags":
67 | [], "childInstruments": []}]}'
68 | headers: {}
69 | status:
70 | code: 200
71 | message: OK
72 | url: https://global-prd-api.hellostake.com/api/products/searchProduct?symbol=SPOT&page=1&max=1
73 | - request:
74 | body: null
75 | headers:
76 | Accept:
77 | - application/json
78 | Content-Type:
79 | - application/json
80 | method: POST
81 | uri: https://global-prd-api.hellostake.com/api/instruments/addRemoveInstrumentWatchlist
82 | response:
83 | body:
84 | string:
85 | '{"instrumentId": "4ef83d08-bd4e-4f2f-883c-6ec43e85e8f6", "watching":
86 | true}'
87 | headers: {}
88 | status:
89 | code: 202
90 | message: Accepted
91 | url: https://global-prd-api.hellostake.com/api/instruments/addRemoveInstrumentWatchlist
92 | version: 1
93 |
--------------------------------------------------------------------------------
/tests/cassettes/test_watchlist/test_remove_from_watchlist.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | body: null
4 | headers:
5 | Accept:
6 | - application/json
7 | Content-Type:
8 | - application/json
9 | method: GET
10 | uri: https://global-prd-api.hellostake.com/api/user
11 | response:
12 | body:
13 | string:
14 | '{"canTradeOnUnsettledFunds": false, "cpfValue": null, "emailVerified":
15 | true, "hasFunded": true, "hasTraded": true, "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
16 | "username": "rita19", "emailAddress": "torresbenjamin@gmail.com", "dw_AccountId":
17 | "1cf93550-8eb4-4c32-a229-826cf8c1be59", "dw_AccountNumber": "w4-1267174s",
18 | "macAccountNumber": "H1-7641957H", "status": null, "macStatus": "BASIC_USER",
19 | "dwStatus": null, "truliooStatus": "APPROVED", "truliooStatusWithWatchlist":
20 | null, "firstName": "Tammy", "lastName": "Alexander", "phoneNumber": "9011530005",
21 | "signUpPhase": 0, "ackSignedWhen": "2021-05-18", "createdDate": 1574303699770,
22 | "stakeApprovedDate": null, "accountType": "INDIVIDUAL", "masterAccountId":
23 | null, "referralCode": "L7-2127933N", "referredByCode": null, "regionIdentifier":
24 | "AUS", "assetSummary": null, "fundingStatistics": null, "tradingStatistics":
25 | null, "w8File": [], "rewardJourneyTimestamp": null, "rewardJourneyStatus":
26 | null, "userProfile": {"residentialAddress": null, "postalAddress": null},
27 | "ledgerBalance": 0.0, "investorAccreditations": null, "proscoreStatus": null,
28 | "fxSpeed": "Regular", "facilitaStatus": null, "dateOfBirth": null, "upToDateDetails2021":
29 | "NO_REQUIREMENTS", "stakeKycStatus": "KYC_APPROVED", "awxMigrationDocsRequired":
30 | null, "documentsStatus": "NO_ACTION", "mfaenabled": false}'
31 | headers: {}
32 | status:
33 | code: 200
34 | message: OK
35 | url: https://global-prd-api.hellostake.com/api/user
36 | - request:
37 | body: null
38 | headers:
39 | Accept:
40 | - application/json
41 | Content-Type:
42 | - application/json
43 | method: GET
44 | uri: https://global-prd-api.hellostake.com/api/products/searchProduct?symbol=SPOT&page=1&max=1
45 | response:
46 | body:
47 | string:
48 | '{"products": [{"id": "93b3755d-0e2d-4d83-8f3b-ddd52cefaeee", "instrumentTypeID":
49 | null, "symbol": "SPOT", "description": "Spotify Technology SA a Luxembourg-based
50 | company, which offers digital music-streaming services. The Company enables
51 | users to discover new releases, which includes the latest singles and albums;
52 | playlists, which includes ready-made playlists put together by music fans
53 | and experts, and over millions of songs so that users can play their favorites,
54 | discover new tracks and build a personalized collection. Users can either
55 | select Spotify Free, which includes only shuffle play or Spotify Premium,
56 | which encompasses a range of features, such as shuffle play, advertisement
57 | free, unlimited skips, listen offline, play any track and high quality audio.
58 | The Company operates through a number of subsidiaries, including Spotify LTD
59 | and is present in over 20 countries.", "category": "Stock", "currencyID":
60 | null, "urlImage": "https://drivewealth.imgix.net/symbols/spot.png?fit=fillmax&w=125&h=125&bg=FFFFFF",
61 | "sector": null, "name": "Spotify Technology SA", "dailyReturn": -3.32, "dailyReturnPercentage":
62 | -1.57, "lastTraded": 207.55, "monthlyReturn": 0.0, "yearlyReturnPercentage":
63 | 92.8, "yearlyReturnValue": 131.68, "popularity": 9470.0, "watched": 8075,
64 | "news": 0, "bought": 9479, "viewed": 23400, "productType": "Instrument", "exchange":
65 | null, "status": "ACTIVE", "type": "EQUITY", "encodedName": "spotify-technology-sa-spot",
66 | "period": "YEAR RETURN", "inceptionDate": 1522713600000, "instrumentTags":
67 | [], "childInstruments": []}]}'
68 | headers: {}
69 | status:
70 | code: 200
71 | message: OK
72 | url: https://global-prd-api.hellostake.com/api/products/searchProduct?symbol=SPOT&page=1&max=1
73 | - request:
74 | body: null
75 | headers:
76 | Accept:
77 | - application/json
78 | Content-Type:
79 | - application/json
80 | method: POST
81 | uri: https://global-prd-api.hellostake.com/api/instruments/addRemoveInstrumentWatchlist
82 | response:
83 | body:
84 | string:
85 | '{"instrumentId": "4ef83d08-bd4e-4f2f-883c-6ec43e85e8f6", "watching":
86 | false}'
87 | headers: {}
88 | status:
89 | code: 202
90 | message: Accepted
91 | url: https://global-prd-api.hellostake.com/api/instruments/addRemoveInstrumentWatchlist
92 | version: 1
93 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import json
3 | import sys
4 | import uuid
5 |
6 | import pytest
7 | from dotenv import load_dotenv
8 | from faker import Faker
9 |
10 | from stake.client import StakeClient
11 |
12 | load_dotenv()
13 |
14 |
15 | @pytest.fixture
16 | async def tracing_client(request, mocker):
17 | async with StakeClient() as client:
18 | yield client
19 |
20 |
21 | @pytest.fixture(scope="session")
22 | def event_loop():
23 | if sys.version_info < (3, 10):
24 | loop = asyncio.get_event_loop()
25 | else:
26 | try:
27 | loop = asyncio.get_running_loop()
28 | except RuntimeError:
29 | loop = asyncio.new_event_loop()
30 |
31 | asyncio.set_event_loop(loop)
32 | yield loop
33 | loop.close()
34 |
35 |
36 | def redact_sensitive_data(response):
37 |
38 | if not response["body"].get("string", None):
39 | response["headers"] = {}
40 | return response
41 |
42 | fake = Faker()
43 | fake.seed_instance(1234)
44 | fake_user_id = "7c9bbfae-0000-47b7-0000-0e66d868c2cf"
45 | fake_order_id = uuid.UUID(str(uuid.uuid3(uuid.NAMESPACE_URL, "test")), version=4)
46 |
47 | fake_transaction_id = f"HHI.{str(fake_order_id)}"
48 | obfuscated_fields = {
49 | "ackSignedWhen": str(fake.date_this_decade()),
50 | "brokerOrderId": 11111,
51 | "buyingPower": 1000.0,
52 | "cashAvailableForTrade": 800,
53 | "cashAvailableForWithdrawal": 1000,
54 | "cashAvailableForTransfer": 1000,
55 | "cashBalance": 1000.0,
56 | "comment": fake.pystr_format(),
57 | "createdWhen": str(fake.date_time_this_decade()),
58 | "dw_AccountId": str(fake_order_id),
59 | "dw_AccountNumber": fake.pystr_format(),
60 | "dw_id": str(fake_order_id),
61 | "dwAccountId": str(fake_order_id),
62 | "dwCashAvailableForWithdrawal": 1000,
63 | "dwOrderId": fake_transaction_id,
64 | "emailAddress": fake.email(),
65 | "finTranID": str(fake_order_id),
66 | "firstName": fake.first_name(),
67 | "id": str(fake_order_id),
68 | "lastName": fake.last_name(),
69 | "liquidCash": 1000.0,
70 | "macAccountNumber": fake.pystr_format(),
71 | "openQty": 100,
72 | "orderID": fake_transaction_id,
73 | "orderId": fake_transaction_id,
74 | "orderNo": fake.pystr_format(),
75 | "password": fake.password(),
76 | "phoneNumber": fake.phone_number(),
77 | "productWatchlistID": str(fake_order_id),
78 | "reference": fake.pystr_format(),
79 | "referenceNumber": fake.pystr_format(),
80 | "referralCode": fake.pystr_format(),
81 | "settledCash": 10000.00,
82 | "tranAmount": 1000,
83 | "tranWhen": str(fake.date_time_this_decade()),
84 | "unrealizedDayPLPercent": 1.0,
85 | "unrealizedPL": 1.0,
86 | "userId": "7c9bbfae-0000-47b7-0000-0e66d868c2cf",
87 | "userID": fake_user_id,
88 | "username": fake.simple_profile()["username"],
89 | "watchlistId": str(fake_order_id),
90 | }
91 |
92 | def _redact_response_body(body):
93 | if not body:
94 | return body
95 |
96 | if isinstance(body, list):
97 | body = [_redact_response_body(res) for res in body]
98 | elif isinstance(body, dict):
99 | for field, value in body.items():
100 | if isinstance(value, list):
101 | body[field] = [_redact_response_body(res) for res in value]
102 | elif isinstance(value, dict):
103 | body[field] = _redact_response_body(value)
104 | else:
105 | body[field] = obfuscated_fields.get(field, value)
106 |
107 | return body
108 |
109 | body = json.loads(response["body"]["string"])
110 |
111 | response["body"]["string"] = bytes(json.dumps(_redact_response_body(body)), "utf-8")
112 |
113 | response["headers"] = {}
114 | return response
115 |
116 |
117 | @pytest.fixture(scope="module")
118 | def vcr_config():
119 | return {
120 | "filter_headers": ["stake-session-token"],
121 | "before_record_response": redact_sensitive_data,
122 | }
123 |
--------------------------------------------------------------------------------
/tests/test_client.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from stake import CredentialsLoginRequest, SessionTokenLoginRequest, StakeClient
4 | from stake.client import InvalidLoginException
5 |
6 |
7 | def test_credentials_login_serializing():
8 |
9 | request = CredentialsLoginRequest(
10 | username="unknown@user.com",
11 | remember_me_days=15,
12 | password="WeirdPassword",
13 | )
14 |
15 | assert request.model_dump(by_alias=True) == {
16 | "username": "unknown@user.com",
17 | "password": "WeirdPassword",
18 | "platformType": "WEB_f5K2x3",
19 | "rememberMeDays": 15,
20 | "otp": None,
21 | }
22 |
23 |
24 | @pytest.mark.asyncio
25 | async def test_credentials_login():
26 | request = CredentialsLoginRequest(
27 | username="unknown@user.com", password="WeirdPassword"
28 | )
29 |
30 | with pytest.raises(InvalidLoginException):
31 | async with StakeClient(request=request) as client:
32 | assert client
33 |
34 | request = SessionTokenLoginRequest(token="invalidtoken002")
35 | with pytest.raises(InvalidLoginException):
36 | async with StakeClient(request=request) as client:
37 | assert client
38 |
--------------------------------------------------------------------------------
/tests/test_equity.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import stake
4 | from stake import constant
5 |
6 |
7 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
8 | @pytest.mark.vcr()
9 | @pytest.mark.asyncio
10 | async def test_list_equities(tracing_client: stake.StakeClient, exchange):
11 |
12 | tracing_client.set_exchange(exchange)
13 | equities = await tracing_client.equities.list()
14 |
15 | assert equities.__class__.__name__ == "EquityPositions"
16 | assert equities.equity_positions
17 |
18 | for e in equities.equity_positions:
19 | assert not (set(e.model_fields.keys()).difference(e.model_fields_set))
20 |
--------------------------------------------------------------------------------
/tests/test_funding.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 |
3 | import pytest
4 |
5 | import stake
6 | from stake import constant
7 | from stake.asx.funding import FundingRequest as ASXFundingRequest
8 | from stake.funding import TransactionRecordRequest as NYSEFundingRequest
9 |
10 |
11 | @pytest.mark.parametrize(
12 | "exchange, request_",
13 | (
14 | (constant.NYSE, NYSEFundingRequest(limit=1000)),
15 | (constant.ASX, ASXFundingRequest(limit=3)),
16 | ),
17 | )
18 | @pytest.mark.vcr()
19 | @pytest.mark.asyncio
20 | async def test_list_fundings(
21 | tracing_client: stake.StakeClient,
22 | exchange: Union[constant.ASXUrl, constant.NYSEUrl],
23 | request_: Union[ASXFundingRequest, NYSEFundingRequest],
24 | ):
25 | tracing_client.set_exchange(exchange=exchange)
26 | await tracing_client.fundings.list(request_)
27 |
28 |
29 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
30 | @pytest.mark.vcr()
31 | @pytest.mark.asyncio
32 | async def test_cash_available(
33 | tracing_client: stake.StakeClient,
34 | exchange: Union[constant.ASXUrl, constant.NYSEUrl],
35 | ):
36 | tracing_client.set_exchange(exchange)
37 | cash_available = await tracing_client.fundings.cash_available()
38 | assert cash_available.cash_available_for_withdrawal == 1000.0
39 |
40 |
41 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
42 | @pytest.mark.vcr()
43 | @pytest.mark.asyncio
44 | async def test_funds_in_flight(
45 | tracing_client: stake.StakeClient,
46 | exchange: Union[constant.ASXUrl, constant.NYSEUrl],
47 | ):
48 | tracing_client.set_exchange(exchange=exchange)
49 | await tracing_client.fundings.in_flight()
50 |
--------------------------------------------------------------------------------
/tests/test_fx.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import stake
4 | from stake import FxConversionRequest
5 |
6 |
7 | @pytest.mark.vcr()
8 | @pytest.mark.asyncio
9 | async def test_fx_conversion(tracing_client: stake.StakeClient):
10 | request = FxConversionRequest(
11 | from_currency="USD", to_currency="AUD", from_amount=1000.0
12 | )
13 | conversion_result = await tracing_client.fx.convert(request)
14 | assert conversion_result.rate > 1.0
15 |
--------------------------------------------------------------------------------
/tests/test_integration.py:
--------------------------------------------------------------------------------
1 | # this module contains tests that run real stake api calls
2 |
3 |
4 | import pytest
5 |
6 | import stake
7 | from stake import constant
8 | from stake.transaction import TransactionRecordRequest
9 |
10 |
11 | @pytest.mark.parametrize("exchange", (constant.NYSE,))
12 | @pytest.mark.asyncio
13 | async def test_integration_NYSE(exchange):
14 | async with stake.StakeClient(exchange=exchange) as session:
15 | await session.watchlist.list_watchlists()
16 | await session.equities.list()
17 | await session.orders.list()
18 | await session.market.get()
19 | await session.market.is_open()
20 | await session.fundings.cash_available()
21 |
22 | request = stake.TransactionRecordRequest(limit=10)
23 | transactions = await session.transactions.list(request)
24 | assert len(transactions) == 10
25 | await session.fundings.list(TransactionRecordRequest(limit=100))
26 |
27 |
28 | @pytest.mark.parametrize("exchange", (constant.ASX,))
29 | @pytest.mark.asyncio
30 | async def test_integration_ASX(exchange):
31 | async with stake.StakeClient(exchange=exchange) as session:
32 | await session.watchlist.list_watchlists()
33 | await session.equities.list()
34 | await session.orders.list()
35 | await session.market.get()
36 | await session.market.is_open()
37 | await session.fundings.cash_available()
38 |
39 | from stake.asx.funding import FundingRequest as ASXFundingRequest
40 | from stake.asx.funding import Sort
41 | from stake.asx.transaction import (
42 | TransactionRecordRequest as ASXTransactionRecordRequest,
43 | )
44 |
45 | request = ASXFundingRequest(
46 | limit=10, sort=[Sort(direction="asc", attribute="insertedAt")]
47 | )
48 | result = await session.fundings.list(request=request)
49 | assert len(result.fundings) == 10
50 |
51 | request = ASXTransactionRecordRequest(
52 | limit=10, sort=[Sort(direction="desc", attribute="insertedAt")]
53 | )
54 | result = await session.transactions.list(request=request)
55 | assert len(result.transactions) == 10
56 |
--------------------------------------------------------------------------------
/tests/test_market.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import stake
4 | from stake import constant
5 |
6 |
7 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
8 | @pytest.mark.vcr()
9 | @pytest.mark.asyncio
10 | async def test_check_market_status(tracing_client: stake.StakeClient, exchange):
11 | tracing_client.set_exchange(exchange)
12 | market_status = await tracing_client.market.get()
13 | assert market_status.status.current == "close"
14 |
--------------------------------------------------------------------------------
/tests/test_order.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from stake import constant
4 | from stake.client import StakeClient
5 |
6 |
7 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
8 | @pytest.mark.vcr()
9 | @pytest.mark.asyncio
10 | async def test_list_orders(tracing_client: StakeClient, exchange):
11 | tracing_client.set_exchange(exchange)
12 | orders = await tracing_client.orders.list()
13 | assert len(orders)
14 | assert orders[0].order_id
15 |
16 |
17 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
18 | @pytest.mark.vcr()
19 | @pytest.mark.asyncio
20 | async def test_cancel_order(tracing_client: StakeClient, exchange):
21 | """Warning, will cancel the first pending order."""
22 | tracing_client.set_exchange(exchange)
23 | orders = await tracing_client.orders.list()
24 | how_many_orders = len(orders)
25 | cancel_order = await tracing_client.orders.cancel(orders[0])
26 | assert cancel_order
27 | orders = await tracing_client.orders.list()
28 | assert len(orders) == how_many_orders - 1
29 |
30 |
31 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
32 | @pytest.mark.vcr()
33 | @pytest.mark.asyncio
34 | async def test_brokerage(tracing_client: StakeClient, exchange):
35 | tracing_client.set_exchange(exchange)
36 | brokerage = await tracing_client.orders.brokerage(order_amount=1.0)
37 | assert brokerage
38 |
--------------------------------------------------------------------------------
/tests/test_product.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from typing import List
3 |
4 | import aiohttp
5 | import pytest
6 | from pydantic import BaseModel
7 |
8 | from stake import asx, constant
9 | from stake.client import HttpClient, StakeClient
10 | from stake.constant import NYSE
11 | from stake.product import Product
12 |
13 |
14 | @pytest.mark.parametrize("exchange", (constant.NYSE, constant.ASX))
15 | @pytest.mark.vcr()
16 | @pytest.mark.asyncio
17 | async def test_find_products_by_name(tracing_client: StakeClient, exchange):
18 | from stake.product import ProductSearchByName
19 |
20 | tracing_client.set_exchange(exchange)
21 | request = ProductSearchByName(keyword="techno")
22 | products = await tracing_client.products.search(request)
23 | assert len(products) == 10
24 | assert products[0].__class__.__name__ == "Instrument"
25 |
26 |
27 | @pytest.mark.asyncio
28 | async def test_product_serializer():
29 |
30 | async with aiohttp.ClientSession(raise_for_status=True) as session:
31 | await session.get(HttpClient.url(NYSE.symbol.format(symbol="MSFT")))
32 |
33 | async def _get_symbol(symbol):
34 | response = await session.get(
35 | HttpClient.url(NYSE.symbol.format(symbol=symbol))
36 | )
37 | return await response.json()
38 |
39 | coros = [_get_symbol(symbol) for symbol in {"MSFT", "TSLA", "GOOG"}]
40 |
41 | results = await asyncio.gather(*coros)
42 | assert [
43 | Product(**serialized_product["products"][0])
44 | for serialized_product in results
45 | ]
46 |
47 |
48 | @pytest.mark.parametrize(
49 | "exchange, symbols",
50 | ((constant.NYSE, ["TSLA", "MSFT", "GOOG"]), (constant.ASX, ["ANZ", "WDS", "COL"])),
51 | )
52 | @pytest.mark.vcr()
53 | @pytest.mark.asyncio
54 | async def test_get_product(
55 | tracing_client: StakeClient, exchange: BaseModel, symbols: List[str]
56 | ):
57 | tracing_client.set_exchange(exchange)
58 | for symbol in symbols:
59 | product = await tracing_client.products.get(symbol)
60 | assert product.__class__.__name__ == "Product"
61 |
62 |
63 | @pytest.mark.parametrize(
64 | "exchange, keyword", ((constant.ASX, "CBA"), (constant.NYSE, "MSFT"))
65 | )
66 | @pytest.mark.asyncio
67 | async def test_search_products(
68 | tracing_client: StakeClient, exchange: BaseModel, keyword: str
69 | ):
70 | tracing_client.set_exchange(exchange)
71 | search_results = await tracing_client.products.search(
72 | asx.ProductSearchByName(keyword=keyword)
73 | )
74 | assert search_results
75 |
76 | product = await tracing_client.products.product_from_instrument(search_results[0])
77 | assert product
78 |
--------------------------------------------------------------------------------
/tests/test_ratings.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import stake
4 | from stake import RatingsRequest
5 |
6 |
7 | @pytest.mark.vcr()
8 | @pytest.mark.asyncio
9 | async def test_list_ratings(tracing_client: stake.StakeClient):
10 | request = RatingsRequest(symbols=["AAPL", "MSFT"], limit=4)
11 | ratings = await tracing_client.ratings.list(request)
12 | assert len(ratings) == 4
13 | assert ratings[0].symbol in ("AAPL", "MSFT")
14 |
15 |
16 | @pytest.mark.vcr()
17 | @pytest.mark.asyncio
18 | async def test_list_ratings_unknown(tracing_client: stake.StakeClient):
19 | request = RatingsRequest(symbols=["NOTEXIST"], limit=4)
20 | ratings = await tracing_client.ratings.list(request)
21 | assert len(ratings) == 0
22 |
--------------------------------------------------------------------------------
/tests/test_trade.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import stake
4 | from stake import constant
5 | from stake.asx import trade as asx_trade
6 | from stake.trade import LimitBuyRequest, MarketBuyRequest, StopBuyRequest
7 |
8 |
9 | @pytest.mark.vcr()
10 | @pytest.mark.asyncio
11 | async def test_stop_buy(tracing_client: stake.StakeClient):
12 | # amountCash too low.
13 | with pytest.raises(ValueError):
14 | await tracing_client.trades.buy(
15 | StopBuyRequest(symbol="AAPL", price=400, amountCash=5)
16 | )
17 |
18 | # price too low.
19 | with pytest.raises(RuntimeError):
20 | await tracing_client.trades.buy(
21 | StopBuyRequest(symbol="AMD", price=10, amountCash=10)
22 | )
23 |
24 |
25 | @pytest.mark.vcr()
26 | @pytest.mark.asyncio
27 | async def test_limit_buy(tracing_client: stake.StakeClient):
28 | with pytest.raises(RuntimeError):
29 | await tracing_client.trades.buy(
30 | LimitBuyRequest(symbol="AAPL", limitPrice=460, quantity=1000)
31 | )
32 |
33 |
34 | @pytest.mark.vcr()
35 | @pytest.mark.asyncio
36 | async def test_limit_sell(tracing_client: stake.StakeClient):
37 | with pytest.raises(RuntimeError):
38 | await tracing_client.trades.sell(
39 | LimitBuyRequest(symbol="AAPL", limitPrice=400, quantity=100)
40 | )
41 |
42 |
43 | @pytest.mark.parametrize(
44 | "exchange, request_",
45 | (
46 | (
47 | constant.NYSE,
48 | MarketBuyRequest(symbol="TSLA", amount_cash=20, comment="from cloud"),
49 | ),
50 | (constant.ASX, asx_trade.MarketBuyRequest(symbol="COL", units=20)),
51 | (
52 | constant.NYSE,
53 | LimitBuyRequest(
54 | symbol="TSLA", limit_price=120, quantity=1, comment="from cloud"
55 | ),
56 | ),
57 | (constant.ASX, asx_trade.LimitBuyRequest(symbol="COL", units=20, price=12.0)),
58 | ),
59 | )
60 | @pytest.mark.asyncio
61 | @pytest.mark.vcr()
62 | async def test_successful_trade(tracing_client: stake.StakeClient, exchange, request_):
63 | tracing_client.set_exchange(exchange)
64 |
65 | trade = await tracing_client.trades.buy(request_)
66 | assert trade
67 |
68 | orders = await tracing_client.orders.list()
69 |
70 | # cancel the order
71 | await tracing_client.orders.cancel(orders[0])
72 |
73 |
74 | @pytest.mark.parametrize(
75 | "exchange, request_",
76 | (
77 | (constant.ASX, asx_trade.MarketSellRequest(symbol="COL", units=20)),
78 | (constant.ASX, asx_trade.LimitSellRequest(symbol="COL", units=20, price=15.0)),
79 | ),
80 | )
81 | @pytest.mark.asyncio
82 | @pytest.mark.vcr()
83 | async def test_sell(tracing_client: stake.StakeClient, exchange, request_):
84 | tracing_client.set_exchange(exchange)
85 |
86 | trade = await tracing_client.trades.sell(request_)
87 | assert trade
88 |
89 | orders = await tracing_client.orders.list()
90 | assert orders
91 | # cancel the order
92 | await tracing_client.orders.cancel(orders[0])
93 |
--------------------------------------------------------------------------------
/tests/test_transaction.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 |
3 | import pytest
4 |
5 | import stake
6 | from stake import constant, transaction
7 | from stake.asx import transaction as asx_transaction
8 |
9 |
10 | @pytest.mark.parametrize(
11 | "exchange, request_",
12 | (
13 | (constant.NYSE, transaction.TransactionRecordRequest(limit=3)),
14 | (constant.ASX, asx_transaction.TransactionRecordRequest(limit=3)),
15 | ),
16 | )
17 | @pytest.mark.vcr()
18 | @pytest.mark.asyncio
19 | async def test_list_transactions(
20 | tracing_client: stake.StakeClient,
21 | exchange: Union[constant.ASXUrl, constant.NYSEUrl],
22 | request_: Union[
23 | asx_transaction.TransactionRecordRequest, transaction.TransactionRecordRequest
24 | ],
25 | ):
26 | tracing_client.set_exchange(exchange)
27 | transactions = await tracing_client.transactions.list(request_)
28 |
29 | assert transactions
30 |
--------------------------------------------------------------------------------
/tests/test_watchlist.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pydantic import BaseModel
3 |
4 | import stake
5 | from stake import constant
6 | from stake.watchlist import (
7 | AddToWatchlistRequest,
8 | CreateWatchlistRequest,
9 | DeleteWatchlistRequest,
10 | RemoveFromWatchlistRequest,
11 | UpdateWatchlistRequest,
12 | Watchlist,
13 | )
14 |
15 | # flake8: noqa
16 |
17 |
18 | @pytest.mark.vcr()
19 | @pytest.mark.asyncio
20 | async def test_add_to_watchlist(tracing_client: stake.StakeClient):
21 | added = await tracing_client.watchlist.add(AddToWatchlistRequest(symbol="SPOT"))
22 | assert added.watching
23 |
24 |
25 | @pytest.mark.vcr()
26 | @pytest.mark.asyncio
27 | async def test_remove_from_watchlist(tracing_client: stake.StakeClient):
28 |
29 | removed = await tracing_client.watchlist.remove(
30 | RemoveFromWatchlistRequest(symbol="SPOT")
31 | )
32 | assert not removed.watching
33 |
34 |
35 | @pytest.mark.vcr()
36 | @pytest.mark.asyncio
37 | async def test_list_watchlist(tracing_client: stake.StakeClient):
38 | watched = await tracing_client.watchlist.list()
39 | assert len(watched) == 10
40 |
41 |
42 | @pytest.mark.parametrize(
43 | "exchange, symbols",
44 | (
45 | (constant.NYSE, ["TSLA", "GOOG", "MSFT", "NOK"]),
46 | (constant.ASX, ["COL", "WDS", "BHP", "OOO"]),
47 | ),
48 | )
49 | @pytest.mark.vcr()
50 | @pytest.mark.asyncio
51 | async def test_create_watchlist(
52 | tracing_client: stake.StakeClient, exchange: BaseModel, symbols: str
53 | ):
54 | name = f"test_watchlist__{exchange.__class__.__name__}"
55 | tracing_client.set_exchange(exchange)
56 | watched = await tracing_client.watchlist.create_watchlist(
57 | CreateWatchlistRequest(name=name)
58 | )
59 | if not watched:
60 | return
61 | assert len(symbols[:3]) == 3
62 | update_request = UpdateWatchlistRequest(
63 | id=watched.watchlist_id, tickers=symbols[:3]
64 | )
65 |
66 | watched = await tracing_client.watchlist.add_to_watchlist(request=update_request)
67 | assert watched.count == 3
68 | # update again, with the same symbols, nothing should change.
69 | watched = await tracing_client.watchlist.add_to_watchlist(request=update_request)
70 | assert watched.count == 3
71 |
72 | update_request = UpdateWatchlistRequest(
73 | id=watched.watchlist_id,
74 | tickers=symbols,
75 | )
76 | watched = await tracing_client.watchlist.remove_from_watchlist(
77 | request=update_request
78 | )
79 |
80 | assert watched.count == 0
81 |
82 | result = await tracing_client.watchlist.delete_watchlist(
83 | request=DeleteWatchlistRequest(id=update_request.id)
84 | )
85 | assert result
86 |
--------------------------------------------------------------------------------