Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
- uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: 3.10
- name: Run image
uses: abatilo/actions-poetry@v2.0.0
with:
poetry-version: 1.3.2
poetry-version: 2.1
- name: Publish python package
run: poetry version ${{ steps.vars.outputs.tag }} && poetry config pypi-token.pypi ${{ secrets.PYPI_API_KEY }} && poetry build && poetry publish
- name: Auto commit pyproject.toml
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
poetry-version: [1.3.2]
python-version: ["3.10", "3.11", "3.12", "3.13"]
poetry-version: [2.1]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -28,19 +28,19 @@ jobs:
STAKE_TOKEN: ${{ secrets.STAKE_TOKEN }}
run: poetry install && poetry run pytest --cov=stake --cov-report=xml && poetry run coverage-badge -f -o coverage.svg
- name: Upload coverage
uses: actions/upload-artifact@v2
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 }}
uses: actions/upload-artifact@v4
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.10 }}
with:
name: coverage
path: coverage.svg
- name: Auto commit coverage badge
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 }}
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.10 }}
uses: EndBug/add-and-commit@v4.4.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run codacy-coverage-reporter
uses: codacy/codacy-coverage-reporter-action@master
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 }}
if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.10 }}
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: coverage.xml
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,5 @@ STAKE.*.json
.idea
DS_Store
.DS_Store

.idx/
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
repos:
- repo: "https://github.com/pre-commit/pre-commit-hooks"
rev: v4.3.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: requirements-txt-fixer
- repo: "https://github.com/psf/black"
rev: 22.6.0
rev: 25.1.0
hooks:
- id: black
- repo: "https://github.com/pre-commit/mirrors-mypy"
rev: v0.961
rev: v1.16.1
hooks:
- id: mypy
- repo: "https://github.com/PyCQA/isort"
rev: 5.12.0
rev: 6.0.1
hooks:
- id: isort
- repo: "https://github.com/asottile/seed-isort-config"
rev: v2.2.0
hooks:
- id: seed-isort-config
- repo: "https://github.com/myint/docformatter"
rev: v1.4
rev: v1.7.7
hooks:
- id: docformatter
args:
- "--in-place"
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: "v0.0.254"
rev: "v0.12.2"
hooks:
- id: ruff
- repo: https://github.com/pre-commit/mirrors-prettier
Expand Down
2,345 changes: 1,261 additions & 1,084 deletions poetry.lock

Large diffs are not rendered by default.

19 changes: 9 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,30 @@ license = "Apache-2.0"
keywords = ["stake","trading","stocks","financial","python"]

[tool.poetry.dependencies]
python = ">=3.8,<4.0.0"
python = ">=3.10,<4.0.0"
python-dotenv = "^0.13.0"
pydantic = "^2.3"
inflection = "^0.5.0"
aiohttp = "^3.9"
single-version = "^1.2.2"

[tool.poetry.dev-dependencies]
pytest = "^7.2.0"
pytest-asyncio = "^0.14.0"

[tool.poetry.group.dev.dependencies]
bump-pydantic = "^0.8.0"
pytest = "^8.4.1"
pytest-asyncio = "^1.0.0"
pre-commit-hooks = "^3.1.0"
pre-commit = "^2.12.0"
pre-commit = "^4.2.0"
pytest-coverage = "^0.0"
black = {version = "^19.10b0", allow-prereleases = true}
pytest-mock = "^3.3.0"
faker = "^4.14.0"
coverage-badge = "^1.0.1"
pytest-recording = "^0.12.0"
pytest-recording = "^0.13.4"

[tool.poetry.group.dev.dependencies]
bump-pydantic = "^0.8.0"

[tool.isort]
profile = "black"
known_third_party = ["aiohttp", "dotenv", "faker", "inflection", "pydantic", "pytest", "single_version"]
known_third_party = ["aiohttp", "dotenv", "faker", "inflection", "pydantic", "pytest", "pytest_asyncio", "single_version"]
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
Expand Down
1 change: 1 addition & 0 deletions stake/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .order import * # noqa: F401, F403
from .product import * # noqa: F401, F403
from .ratings import * # noqa: F401, F403
from .statement import * # noqa: F401, F403
from .trade import * # noqa: F401, F403
from .transaction import * # noqa: F401, F403
from .watchlist import * # noqa: F401, F403
Expand Down
4 changes: 2 additions & 2 deletions stake/asx/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ class Side(str, Enum):
class TradeType(str, Enum):
"""The type of trade the user is requesting."""

MARKET: str = "MARKET_TO_LIMIT"
LIMIT: str = "LIMIT"
MARKET = "MARKET_TO_LIMIT"
LIMIT = "LIMIT"
11 changes: 7 additions & 4 deletions stake/asx/market.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import date
from datetime import datetime
from typing import Optional

import pydantic
Expand All @@ -10,11 +10,11 @@


class Status(pydantic.BaseModel):
current: Optional[str] = None
current: str


class MarketStatus(pydantic.BaseModel):
last_trading_date: Optional[date] = None
last_trading_date: Optional[datetime] = None
status: Status
model_config = ConfigDict(alias_generator=camelcase)

Expand All @@ -24,7 +24,10 @@ class MarketClient(BaseClient):

async def get(self) -> MarketStatus:
data = await self._client.get(self._client.exchange.market_status)
return MarketStatus(**data)
return MarketStatus(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Directly indexing data[0] assumes a non-empty list response.

This could raise an IndexError if the list is empty. Please add a check to handle empty responses.

last_trading_date=data[0]["lastTradedTimestamp"],
status=Status(current=data[0]["marketStatus"]),
)

async def is_open(self) -> bool:
status = await self.get()
Expand Down
4 changes: 2 additions & 2 deletions stake/asx/trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
class ExpiryDate(str, Enum):
"""The expiry date for the trade."""

IN_ONE_DAY: str = "GFD"
IN_THIRTY_DAYS: str = "GTC"
IN_ONE_DAY = "GFD"
IN_THIRTY_DAYS = "GTC"


class GenericTradeRequest(BaseModel):
Expand Down
4 changes: 2 additions & 2 deletions stake/asx/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@


class SortDirection(str, Enum):
ASC: str = "asc"
DESC: str = "desc"
ASC = "asc"
DESC = "desc"


class Sort(BaseModel):
Expand Down
55 changes: 31 additions & 24 deletions stake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
order,
product,
ratings,
statement,
trade,
transaction,
user,
Expand Down Expand Up @@ -70,7 +71,9 @@ def url(endpoint: str) -> str:
return endpoint

@staticmethod
async def get(url: str, payload: dict = None, headers: dict = None) -> dict:
async def get(
url: str, payload: dict | None = None, headers: dict | None = None
) -> dict:
async with aiohttp.ClientSession(
headers=headers, raise_for_status=True
) as session:
Expand All @@ -80,7 +83,7 @@ async def get(url: str, payload: dict = None, headers: dict = None) -> dict:
return await response.json()

@staticmethod
async def post(url: str, payload: dict, headers: dict = None) -> dict:
async def post(url: str, payload: dict, headers: dict | None = None) -> dict:

async with aiohttp.ClientSession(
headers=headers, raise_for_status=True
Expand All @@ -92,7 +95,9 @@ async def post(url: str, payload: dict, headers: dict = None) -> dict:
return await response.json()

@staticmethod
async def delete(url: str, payload: dict = None, headers: dict = None) -> dict:
async def delete(
url: str, payload: dict | None = None, headers: dict | None = None
) -> dict:
async with aiohttp.ClientSession(
headers=headers, raise_for_status=True
) as session:
Expand All @@ -111,7 +116,7 @@ class StakeClient:

def __init__(
self,
request: Union[CredentialsLoginRequest, SessionTokenLoginRequest] = None,
request: Union[CredentialsLoginRequest, SessionTokenLoginRequest, None] = None,
exchange: Union[constant.NYSEUrl, constant.ASXUrl] = constant.NYSE,
):
"""
Expand All @@ -137,28 +142,29 @@ def set_exchange(self, exchange: Union[constant.NYSEUrl, constant.ASXUrl]) -> No
self.watchlist: watchlist.WatchlistClient = watchlist.WatchlistClient(self)

if exchange == constant.ASX:
self.equities: Union[
asx.equity.EquitiesClient, equity.EquitiesClient
] = asx.equity.EquitiesClient(self)
self.fundings: Union[
asx.funding.FundingsClient, funding.FundingsClient
] = asx.funding.FundingsClient(self)
self.market: Union[
asx.market.MarketClient, market.MarketClient
] = asx.market.MarketClient(self)
self.orders: Union[
asx.order.OrdersClient, order.OrdersClient
] = asx.order.OrdersClient(self)
self.products: Union[
asx.product.ProductsClient, product.ProductsClient
] = asx.product.ProductsClient(self)
self.trades: Union[
asx.trade.TradesClient, trade.TradesClient
] = asx.trade.TradesClient(self)
self.equities: Union[asx.equity.EquitiesClient, equity.EquitiesClient] = (
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Extract code out into method [×2] (extract-method)

asx.equity.EquitiesClient(self)
)
self.fundings: Union[asx.funding.FundingsClient, funding.FundingsClient] = (
asx.funding.FundingsClient(self)
)
self.market: Union[asx.market.MarketClient, market.MarketClient] = (
asx.market.MarketClient(self)
)
self.orders: Union[asx.order.OrdersClient, order.OrdersClient] = (
asx.order.OrdersClient(self)
)
self.products: Union[asx.product.ProductsClient, product.ProductsClient] = (
asx.product.ProductsClient(self)
)
self.trades: Union[asx.trade.TradesClient, trade.TradesClient] = (
asx.trade.TradesClient(self)
)
self.transactions: Union[
asx.transaction.TransactionsClient, transaction.TransactionsClient
] = asx.transaction.TransactionsClient(self)
# ratings is unsupported.
# statements is unsupported.
else:
self.equities = equity.EquitiesClient(self)
self.fundings = funding.FundingsClient(self)
Expand All @@ -169,8 +175,9 @@ def set_exchange(self, exchange: Union[constant.NYSEUrl, constant.ASXUrl]) -> No
self.ratings = ratings.RatingsClient(self)
self.trades = trade.TradesClient(self)
self.transactions = transaction.TransactionsClient(self)
self.statements = statement.StatementClient(self)

async def get(self, url: str, payload: dict = None) -> dict:
async def get(self, url: str, payload: dict | None = None) -> dict:
"""Performs an HTTP get operation.

Args:
Expand Down Expand Up @@ -200,7 +207,7 @@ async def post(self, url: str, payload: dict) -> dict:
url, payload=payload, headers=self.headers.model_dump(by_alias=True)
)

async def delete(self, url: str, payload: dict = None) -> dict:
async def delete(self, url: str, payload: dict | None = None) -> dict:
"""Performs an HTTP delete operation.

Args:
Expand Down
10 changes: 9 additions & 1 deletion stake/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ class NYSEUrl(BaseModel):
)
update_watchlist: str = read_watchlist + "/items"

statement: str = urljoin(
STAKE_URL,
"data/fundamentals/{symbol}/statements?startDate={date}",
allow_fragments=True,
)


NYSE = NYSEUrl()

Expand All @@ -107,7 +113,9 @@ class ASXUrl(BaseModel):
equity_positions: str = urljoin(
ASX_STAKE_URL, "instrument/equityPositions", allow_fragments=True
)
market_status: str = "https://early-bird-promo.hellostake.com/marketStatus"
market_status: str = urljoin(
ASX_STAKE_URL, "instrument/quoteTwo/ASX", allow_fragments=True
)

orders: str = urljoin(ASX_STAKE_URL, "orders", allow_fragments=True)

Expand Down
1 change: 1 addition & 0 deletions stake/funding.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Your current fundings."""

import asyncio
import json
from datetime import datetime
Expand Down
5 changes: 3 additions & 2 deletions stake/fx.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Currency conversion."""

from enum import Enum

from pydantic import BaseModel, ConfigDict
Expand All @@ -10,8 +11,8 @@


class CurrencyEnum(str, Enum):
AUD: str = "AUD"
USD: str = "USD"
AUD = "AUD"
USD = "USD"


class FxConversionRequest(BaseModel):
Expand Down
1 change: 0 additions & 1 deletion stake/ratings.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,4 @@ async def list(self, request: RatingsRequest) -> List[Rating]:

if data == {"message": "No data returned"}:
return []
print(data["ratings"])
return [Rating(**d) for d in data["ratings"]]
Loading
Loading