Skip to content

Commit 1993392

Browse files
authored
Merge pull request #420 from alpacahq/feature/v2-latest
Add latest trade and quote
2 parents 4b7b2b2 + cffbc36 commit 1993392

File tree

3 files changed

+116
-4
lines changed

3 files changed

+116
-4
lines changed

alpaca_trade_api/entity_v2.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from enum import Enum
22
import pandas as pd
3-
from .entity import Bar, Trade, Quote
4-
3+
from .entity import Bar, Entity, Trade, Quote, _NanoTimestamped
4+
from typing import Dict
55

66
trade_mapping_v2 = {
77
"i": "id",
@@ -66,6 +66,17 @@ def df(self):
6666
return self._df
6767

6868

69+
class Remapped:
70+
def __init__(self, mapping: Dict[str,str], *args, **kwargs):
71+
self._reversed_mapping = {value: key for (key, value) in mapping.items()}
72+
super().__init__(*args, **kwargs)
73+
74+
def __getattr__(self, key):
75+
if key in self._reversed_mapping:
76+
return super().__getattr__(self._reversed_mapping[key])
77+
return super().__getattr__(key)
78+
79+
6980
class BarsV2(EntityList):
7081
def __init__(self, raw):
7182
super().__init__(EntityListType.Bar, raw)
@@ -79,3 +90,17 @@ def __init__(self, raw):
7990
class QuotesV2(EntityList):
8091
def __init__(self, raw):
8192
super().__init__(EntityListType.Quote, raw)
93+
94+
95+
class TradeV2(Remapped, _NanoTimestamped, Entity):
96+
_tskeys = ('t',)
97+
98+
def __init__(self, raw):
99+
super().__init__(trade_mapping_v2, raw)
100+
101+
102+
class QuoteV2(Remapped, _NanoTimestamped, Entity):
103+
_tskeys = ('t',)
104+
105+
def __init__(self,raw):
106+
super().__init__(quote_mapping_v2, raw)

alpaca_trade_api/rest.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
Asset, Order, Position, BarSet, Clock, Calendar,
1717
Aggs, Trade, Quote, Watchlist, PortfolioHistory
1818
)
19-
from .entity_v2 import BarsV2, TradesV2, QuotesV2
19+
from .entity_v2 import BarsV2, TradesV2, TradeV2, QuotesV2, QuoteV2
2020
from . import polygon
2121

2222
logger = logging.getLogger(__name__)
@@ -520,7 +520,7 @@ def get_last_trade(self, symbol: str) -> Trade:
520520
return self.response_wrapper(resp['last'], Trade)
521521

522522
def get_last_quote(self, symbol: str) -> Quote:
523-
"""Get the last trade for the given symbol"""
523+
"""Get the last quote for the given symbol"""
524524
resp = self.data_get('/last_quote/stocks/{}'.format(symbol))
525525
return self.response_wrapper(resp['last'], Quote)
526526

@@ -636,6 +636,22 @@ def get_bars(self,
636636
raw=True))
637637
return BarsV2(bars)
638638

639+
def get_latest_trade(self, symbol: str) -> TradeV2:
640+
"""
641+
Get the latest trade for the given symbol
642+
"""
643+
resp = self.data_get(
644+
'/stocks/{}/trades/latest'.format(symbol),
645+
api_version='v2')
646+
return self.response_wrapper(resp['trade'], TradeV2)
647+
648+
def get_latest_quote(self, symbol: str) -> QuoteV2:
649+
"""Get the latest quote for the given symbol"""
650+
resp = self.data_get(
651+
'/stocks/{}/quotes/latest'.format(symbol),
652+
api_version='v2')
653+
return self.response_wrapper(resp['quote'], QuoteV2)
654+
639655
def get_clock(self) -> Clock:
640656
resp = self.get('/clock')
641657
return self.response_wrapper(resp, Clock)

tests/test_rest.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,77 @@ def test_data(reqmock):
541541
assert type(quote) == tradeapi.entity.Quote
542542
assert type(api_raw.get_last_quote('AAPL')) == dict
543543

544+
# Latest trade
545+
reqmock.get(
546+
'https://data.alpaca.markets/v2/stocks/AAPL/trades/latest',
547+
text='''
548+
{
549+
"symbol": "AAPL",
550+
"trade": {
551+
"t": "2021-04-20T12:40:34.123456789Z",
552+
"x": "J",
553+
"p": 134.7,
554+
"s": 20,
555+
"c": [
556+
"@",
557+
"T",
558+
"I"
559+
],
560+
"i": 32,
561+
"z": "C"
562+
}
563+
}
564+
'''
565+
)
566+
latest_trade = api.get_latest_trade('AAPL')
567+
assert latest_trade.exchange == "J"
568+
assert latest_trade.price == 134.7
569+
assert latest_trade.size == 20
570+
assert latest_trade.conditions == ["@", "T", "I"]
571+
assert latest_trade.id == 32
572+
assert latest_trade.tape == "C"
573+
assert latest_trade.timestamp.day == 20
574+
assert latest_trade.timestamp.second == 34
575+
assert latest_trade.timestamp.nanosecond == 789
576+
assert type(latest_trade) == tradeapi.entity_v2.TradeV2
577+
assert type(api_raw.get_latest_trade('AAPL')) == dict
578+
579+
# Latest quote
580+
reqmock.get(
581+
'https://data.alpaca.markets/v2/stocks/AAPL/quotes/latest',
582+
text='''
583+
{
584+
"symbol": "AAPL",
585+
"quote": {
586+
"t": "2021-04-20T13:01:57.123456789",
587+
"ax": "Q",
588+
"ap": 134.68,
589+
"as": 1,
590+
"bx": "K",
591+
"bp": 134.66,
592+
"bs": 29,
593+
"c": [
594+
"R"
595+
],
596+
"z": "C"
597+
}
598+
}'''
599+
)
600+
601+
latest_quote = api.get_latest_quote('AAPL')
602+
assert latest_quote.ask_exchange == "Q"
603+
assert latest_quote.ask_price == 134.68
604+
assert latest_quote.ask_size == 1
605+
assert latest_quote.bid_exchange == "K"
606+
assert latest_quote.bid_price == 134.66
607+
assert latest_quote.bid_size == 29
608+
assert latest_quote.conditions == ["R"]
609+
assert latest_quote.tape == "C"
610+
assert latest_quote.timestamp.day == 20
611+
assert latest_quote.timestamp.nanosecond == 789
612+
assert type(latest_quote) == tradeapi.entity_v2.QuoteV2
613+
assert type(api_raw.get_latest_quote('AAPL')) == dict
614+
544615

545616
def test_watchlists(reqmock):
546617
api = tradeapi.REST('key-id', 'secret-key', api_version='v1')

0 commit comments

Comments
 (0)