Skip to content

feat: Add auction data handling to stock historical data client #576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
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
1 change: 1 addition & 0 deletions alpaca/common/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ def _get_marketdata_entries(response: HTTPResult, no_sub_key: bool) -> RawData:
"snapshots",
"trade",
"trades",
"auctions",
}
selected_keys = data_keys.intersection(response)
# Neither of these should ever happen!
Expand Down
4 changes: 2 additions & 2 deletions alpaca/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def validate_uuid_id_param(


def validate_symbol_or_asset_id(
symbol_or_asset_id: Union[UUID, str]
symbol_or_asset_id: Union[UUID, str],
) -> Union[UUID, str]:
"""
A helper function to eliminate duplicate checks of symbols or asset ids.
Expand All @@ -55,7 +55,7 @@ def validate_symbol_or_asset_id(


def validate_symbol_or_contract_id(
symbol_or_contract_id: Union[UUID, str]
symbol_or_contract_id: Union[UUID, str],
) -> Union[UUID, str]:
"""
A helper function to eliminate duplicate checks of symbols or contract id.
Expand Down
22 changes: 21 additions & 1 deletion alpaca/data/historical/stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from alpaca.data.historical.utils import (
parse_obj_as_symbol_dict,
)
from alpaca.data.models import BarSet, QuoteSet, TradeSet
from alpaca.data.models import BarSet, QuoteSet, TradeSet, AuctionSet
from alpaca.data.requests import (
StockBarsRequest,
StockLatestBarRequest,
Expand All @@ -19,6 +19,7 @@
StockQuotesRequest,
StockSnapshotRequest,
StockTradesRequest,
StockAuctionsRequest,
)


Expand Down Expand Up @@ -72,6 +73,25 @@ def __init__(
raw_data=raw_data,
)

def get_stock_auctions(self, request_params: StockAuctionsRequest):
"""Returns auction data for an equity or list of equities over a given time period.

Args:
request_params (StockAuctionsRequest): The request object for retrieving stock auction data.

Returns:
Union[AuctionSet, RawData]: The auction data either in raw or wrapped form
"""
raw_auctions = self._get_marketdata(
path="/stocks/auctions",
params=request_params.to_request_fields(),
)
Comment on lines +85 to +88
Copy link
Contributor

Choose a reason for hiding this comment

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

We have recently introduced page_size parameter to _get_marketdata() [1], could you please set page_size for auctions as well?

*1 #583

Suggested change
raw_auctions = self._get_marketdata(
path="/stocks/auctions",
params=request_params.to_request_fields(),
)
raw_auctions = self._get_marketdata(
path="/stocks/auctions",
params=request_params.to_request_fields(),
page_limit = 1_000,
page_size = 1_000,
)


if self._use_raw_data:
return raw_auctions

return AuctionSet(raw_auctions, request_params.sort)

def get_stock_bars(
self, request_params: StockBarsRequest
) -> Union[BarSet, RawData]:
Expand Down
9 changes: 9 additions & 0 deletions alpaca/data/mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,12 @@
"cc": "corrected_conditions",
"z": "tape",
}

AUCTION_MAPPING: Dict[str, str] = {
"c": "condition",
"p": "price",
"s": "size",
"t": "timestamp",
"x": "exchange",
"at": "auction_type",
}
1 change: 1 addition & 0 deletions alpaca/data/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from alpaca.data.models.trades import *
from alpaca.data.models.snapshots import *
from alpaca.data.models.orderbooks import *
from alpaca.data.models.auctions import *
108 changes: 108 additions & 0 deletions alpaca/data/models/auctions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from datetime import datetime
from typing import Dict, List
Copy link
Contributor

Choose a reason for hiding this comment

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

to use Optional

Suggested change
from typing import Dict, List
from typing import Dict, List, Optional


from alpaca.common.models import ValidateBaseModel as BaseModel
from alpaca.common.types import RawData
from alpaca.common.enums import Sort
from alpaca.data.mappings import AUCTION_MAPPING
from alpaca.data.models.base import BaseDataSet, TimeSeriesMixin


class Auction(BaseModel):
"""Represents one auction of aggregated trade data over a specified interval.

Attributes:
symbol (str): The ticker identifier for the security whose data forms the bar.
Copy link
Contributor

Choose a reason for hiding this comment

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

could you please add other attribuites to docstring?

Copy link
Author

Choose a reason for hiding this comment

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

all done

timestamp (datetime): The timestamp of the auction.
condition (str): The condition of the auction.
price (float): The price of the auction.
size (float): The size of the auction.
exchange (str): The exchange of the auction.
auction_type (str): The type of auction. OPEN or CLOSE.

"""

symbol: str
timestamp: datetime
condition: str
price: float
size: float
exchange: str
auction_type: str

def __init__(self, symbol: str, raw_data: RawData) -> None:
"""Instantiates an auction

Args:
raw_data (RawData): Raw unparsed auction data from API, contains ohlc and other fields.
"""
mapped_auction = {}

if raw_data is not None:
mapped_auction = {
AUCTION_MAPPING[key]: val
for key, val in raw_data.items()
if key in AUCTION_MAPPING
}

super().__init__(symbol=symbol, **mapped_auction)


class AuctionSet(BaseDataSet, TimeSeriesMixin):
"""A collection of Auctions.

Attributes:
data (Dict[str, List[Auction]]): The collection of Auctions keyed by symbol.
"""

data: Dict[str, List[Auction]] = {}

def __init__(self, raw_data: RawData, sort: Sort) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

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

StockAuctionsRequest.sort is Optional[Sort]

Suggested change
def __init__(self, raw_data: RawData, sort: Sort) -> None:
def __init__(self, raw_data: RawData, sort: Optional[Sort]) -> None:

"""A collection of Auctions.

Args:
raw_data (RawData): The collection of raw auction data from API keyed by Symbol.
sort (Sort): The sort order of the auctions.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
sort (Sort): The sort order of the auctions.
sort (Optional[Sort]): The sort order of the auctions. Default to ASC

"""
parsed_auctions = {}

raw_auctions = raw_data

if raw_auctions is not None:
for symbol, auctions in raw_auctions.items():

auction_data = []
for auction in auctions:
c = auction.get("c")
o = auction.get("o")

if sort == Sort.DESC:
if c is not None:
for close_auction in c:
if close_auction:
Copy link
Contributor

Choose a reason for hiding this comment

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

could we remove this if condition? or do we have possiblity to have None element in the list?

close_auction["at"] = "CLOSE"
auction_data.extend(c)
if o is not None:
for open_auction in o:
if open_auction:
open_auction["at"] = "OPEN"
auction_data.extend(o)
else: # NOTE: None and ASC are the same
if o is not None:
for open_auction in o:
if open_auction:
open_auction["at"] = "OPEN"
auction_data.extend(o)
if c is not None:
for close_auction in c:
if close_auction:
close_auction["at"] = "CLOSE"
auction_data.extend(c)
Comment on lines +76 to +100
Copy link
Contributor

@hiohiohio hiohiohio Apr 17, 2025

Choose a reason for hiding this comment

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

How about below logic? tried to use .get("c", []) but it does not work for me.

Suggested change
c = auction.get("c")
o = auction.get("o")
if sort == Sort.DESC:
if c is not None:
for close_auction in c:
if close_auction:
close_auction["at"] = "CLOSE"
auction_data.extend(c)
if o is not None:
for open_auction in o:
if open_auction:
open_auction["at"] = "OPEN"
auction_data.extend(o)
else: # NOTE: None and ASC are the same
if o is not None:
for open_auction in o:
if open_auction:
open_auction["at"] = "OPEN"
auction_data.extend(o)
if c is not None:
for close_auction in c:
if close_auction:
close_auction["at"] = "CLOSE"
auction_data.extend(c)
c = auction.get("c")
if c is None:
c = []
o = auction.get("o")
if o is None:
o = []
for close_auction in c:
close_auction["at"] = "CLOSE"
for open_auction in o:
open_auction["at"] = "OPEN"
if sort == Sort.DESC:
auction_data.extend(c)
auction_data.extend(o)
else: # NOTE: None and ASC are the same
auction_data.extend(o)
auction_data.extend(c)

Copy link
Author

Choose a reason for hiding this comment

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

.get

I think both are fine. c = [] if c is None else c or .get("c", [])

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. Yeah, somehow, .get("c", []) does not work for me. checking with below snippets.

from datetime import datetime
from zoneinfo import ZoneInfo

from alpaca.common.enums import Sort
from alpaca.data.historical.stock import StockAuctionsRequest, StockHistoricalDataClient


api_key="API KEY"
secret_key="SECRET"
client = StockHistoricalDataClient(api_key, secret_key)

req = StockAuctionsRequest(
    symbol_or_symbols=["SPY"],
    start=datetime(2025, 4,2),
    end=datetime(2025, 4, 9, tzinfo=ZoneInfo("America/New_York")),
    sort=Sort.DESC,
    )
ret = client.get_stock_auctions(req)


parsed_auctions[symbol] = [
Comment on lines +79 to +102
Copy link
Author

Choose a reason for hiding this comment

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

removed the sorting func and ustilize the asc/desc in requesting

Copy link
Contributor

@hiohiohio hiohiohio Apr 17, 2025

Choose a reason for hiding this comment

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

Thank you! By the way, what is the meaning of if open_auction/if close_auction? Do you have observed something we need to handle the empty element? I felt we use for in the code, the element should have value?

Auction(symbol, auction)
for auction in auction_data
if auction is not None
]

super().__init__(data=parsed_auctions)
23 changes: 23 additions & 0 deletions alpaca/data/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ def __init__(self, **data: Any) -> None:
super().__init__(**data)


# ############################## Auctions ################################# #


class StockAuctionsRequest(BaseTimeseriesDataRequest):
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please write docstring and also add to api_reference?

"""
The request model for retrieving auction data for stocks.

See BaseTimeseriesDataRequest for more information on available parameters.

Attributes:
symbol_or_symbols (Union[str, List[str]]): The ticker identifier or list of ticker identifiers.
start (Optional[datetime]): The beginning of the time interval for desired data. Timezone naive inputs assumed to be in UTC.
end (Optional[datetime]): The end of the time interval for desired data. Defaults to now. Timezone naive inputs assumed to be in UTC.
limit (Optional[int]): Upper limit of number of data points to return. Defaults to None.
feed (Optional[DataFeed]): The stock data feed to retrieve from.
sort (Optional[Sort]): The chronological order of response based on the timestamp. Defaults to ASC.
asof (Optional[str]): The asof date of the queried stock symbol(s) in YYYY-MM-DD format.
"""

feed: Optional[DataFeed] = None
asof: Optional[str] = None


# ############################## Bars ################################# #


Expand Down
12 changes: 12 additions & 0 deletions docs/api_reference/data/models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ Models
======


Auction
-------

.. autoclass:: alpaca.data.models.auctions.Auction


AuctionSet
----------

.. autoclass:: alpaca.data.models.auctions.AuctionSet


Bar
---

Expand Down
6 changes: 6 additions & 0 deletions docs/api_reference/data/stock/requests.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ StockBarsRequest
.. autoclass:: alpaca.data.requests.StockBarsRequest


StockAuctionsRequest
--------------------

.. autoclass:: alpaca.data.requests.StockAuctionsRequest


StockQuotesRequest
------------------

Expand Down
Loading