Skip to content

Commit 9082483

Browse files
committed
option filter
1 parent 21e88aa commit 9082483

File tree

7 files changed

+144
-40
lines changed

7 files changed

+144
-40
lines changed

tigeropen/common/consts/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .quote_keys import QuoteChangeKey, QuoteKeyType
1313

1414
OPEN_API_SERVICE_VERSION = "2.0"
15+
OPEN_API_SERVICE_VERSION_V3 = "3.0"
1516

1617
THREAD_LOCAL = threading.local()
1718

tigeropen/common/util/string_utils.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"""
77
import re
88

9+
CAMEL_PATTERN = re.compile(r'([a-z]|\d)([A-Z])')
10+
911

1012
def add_start_end(key, start_marker, end_marker):
1113
if key.find(start_marker) < 0:
@@ -16,8 +18,4 @@ def add_start_end(key, start_marker, end_marker):
1618

1719

1820
def camel_to_underline(hunp_str):
19-
p = re.compile(r'([a-z]|\d)([A-Z])')
20-
sub = re.sub(p, r'\1_\2', hunp_str).lower()
21-
return sub
22-
23-
21+
return re.sub(CAMEL_PATTERN, r'\1_\2', hunp_str).lower()

tigeropen/examples/quote_client_demo.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pandas as pd
99
from tigeropen.common.consts import Market, QuoteRight, FinancialReportPeriodType, Valuation, \
1010
Income, Balance, CashFlow, BalanceSheetRatio, Growth, Leverage, Profitability, IndustryLevel
11+
from tigeropen.quote.domain.filter import OptionFilter
1112

1213
from tigeropen.quote.quote_client import QuoteClient
1314

@@ -66,6 +67,19 @@ def get_option_quote():
6667
ticks = openapi_client.get_option_trade_ticks(['AAPL 190104P00134000'])
6768
print(ticks)
6869

70+
# option chain filter
71+
option_filter = OptionFilter(implied_volatility_min=0.5, implied_volatility_max=0.9, delta_min=0, delta_max=1,
72+
open_interest_min=100, gamma_min=0.005, theta_max=-0.05, in_the_money=True)
73+
chains = openapi_client.get_option_chain('AAPL', '2023-01-20', option_filter=option_filter)
74+
print(chains)
75+
# or
76+
chains = openapi_client.get_option_chain('AAPL', '2023-01-20', implied_volatility_min=0.5, open_interest_min=200,
77+
vega_min=0.1, rho_max=0.9)
78+
# convert expiry date to US/Eastern
79+
chains['expiry_date'] = pd.to_datetime(chains['expiry'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
80+
print(chains)
81+
82+
6983

7084
def get_future_quote():
7185
exchanges = openapi_client.get_future_exchanges()

tigeropen/quote/domain/filter.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# @Date : 2021/11/15
4+
# @Author : sukai
5+
6+
GREEKS = ['delta', 'gamma', 'theta', 'vega', 'rho']
7+
8+
9+
class OptionFilter:
10+
def __init__(self, implied_volatility_min=None, implied_volatility_max=None, open_interest_min=None,
11+
open_interest_max=None, delta_min=None, delta_max=None, gamma_min=None, gamma_max=None,
12+
theta_min=None, theta_max=None, vega_min=None, vega_max=None, rho_min=None, rho_max=None,
13+
in_the_money=None):
14+
"""
15+
option filter
16+
:param implied_volatility_min:
17+
:param implied_volatility_max:
18+
:param open_interest_min:
19+
:param open_interest_max:
20+
:param delta_min:
21+
:param delta_max:
22+
:param gamma_min:
23+
:param gamma_max:
24+
:param theta_min:
25+
:param theta_max:
26+
:param vega_min:
27+
:param vega_max:
28+
:param rho_min:
29+
:param rho_max:
30+
:param in_the_money:
31+
"""
32+
self.implied_volatility_min = implied_volatility_min
33+
self.implied_volatility_max = implied_volatility_max
34+
self.open_interest_min = open_interest_min
35+
self.open_interest_max = open_interest_max
36+
self.delta_min = delta_min
37+
self.delta_max = delta_max
38+
self.gamma_min = gamma_min
39+
self.gamma_max = gamma_max
40+
self.theta_min = theta_min
41+
self.theta_max = theta_max
42+
self.vega_min = vega_min
43+
self.vega_max = vega_max
44+
self.rho_min = rho_min
45+
self.rho_max = rho_max
46+
self.in_the_money = in_the_money
47+
48+
def to_dict(self):
49+
return {'greeks': self._get_greeks(),
50+
'implied_volatility': self._min_max('implied_volatility'),
51+
'open_interest': self._min_max('open_interest'),
52+
'in_the_money': self.in_the_money}
53+
54+
def _get_greeks(self):
55+
return {greek: self._min_max(greek) for greek in GREEKS}
56+
57+
def _min_max(self, k):
58+
return {'min': getattr(self, k + '_min'), 'max': getattr(self, k + '_max')}

tigeropen/quote/quote_client.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import delorean
1212

13-
from tigeropen.common.consts import Market, Language, QuoteRight, BarPeriod
13+
from tigeropen.common.consts import Market, Language, QuoteRight, BarPeriod, OPEN_API_SERVICE_VERSION_V3
1414
from tigeropen.common.consts import THREAD_LOCAL, SecurityType, CorporateActionType, IndustryLevel
1515
from tigeropen.common.consts.service_types import GRAB_QUOTE_PERMISSION
1616
from tigeropen.common.consts.service_types import MARKET_STATE, ALL_SYMBOLS, ALL_SYMBOL_NAMES, BRIEF, \
@@ -31,10 +31,11 @@
3131
from tigeropen.fundamental.response.financial_report_response import FinancialReportResponse
3232
from tigeropen.fundamental.response.industry_response import IndustryListResponse, IndustryStocksResponse, \
3333
StockIndustryResponse
34+
from tigeropen.quote.domain.filter import OptionFilter
3435
from tigeropen.quote.request import OpenApiRequest
3536
from tigeropen.quote.request.model import MarketParams, MultipleQuoteParams, MultipleContractParams, \
3637
FutureQuoteParams, FutureExchangeParams, FutureTypeParams, FutureTradingTimeParams, SingleContractParams, \
37-
SingleOptionQuoteParams, DepthQuoteParams
38+
SingleOptionQuoteParams, DepthQuoteParams, OptionChainParams
3839
from tigeropen.quote.response.future_briefs_response import FutureBriefsResponse
3940
from tigeropen.quote.response.future_contract_response import FutureContractResponse
4041
from tigeropen.quote.response.future_exchange_response import FutureExchangeResponse
@@ -508,35 +509,40 @@ def get_option_expirations(self, symbols):
508509

509510
return None
510511

511-
def get_option_chain(self, symbol, expiry):
512-
"""
513-
获取美股期权链
514-
:param symbol: 股票代码
515-
:param expiry: 过期日 ( 类似 '2021-06-18' 或者 1560484800000 )
516-
:return: pandas.DataFrame,各 column 含义如下:
517-
identifier: 期权代码
518-
symbol: 期权对应的正股代码
519-
expiry: 期权到期日,毫秒级别的时间戳
520-
strike: 行权价
521-
put_call: 期权的方向
522-
multiplier: 乘数
523-
ask_price: 卖价
524-
ask_size: 卖量
525-
bid_price: 买价
526-
bid_size: 买量
527-
pre_close: 前收价
528-
latest_price: 最新价
529-
volume: 成交量
530-
open_interest: 未平仓数量
531-
"""
532-
params = MultipleContractParams()
512+
def get_option_chain(self, symbol, expiry, option_filter=None, **kwargs):
513+
"""
514+
query option chain with filter
515+
:param symbol: underlying stock symbol
516+
:param expiry: expiration date ( like '2021-06-18' or 1560484800000 )
517+
:param option_filter: option filter conditions, tigeropen.quote.domain.filter.OptionFilter
518+
:param kwargs: optional. specify option_filter parameters directly without option_filer,
519+
like: open_interest_min=100, delta_min=0.1
520+
:return: pandas.DataFrame,the columns are as follows:
521+
identifier: option identifier
522+
symbol: underlying stock symbol
523+
expiry: option expiration date. timestamp in millisecond, like 1560484800000
524+
strike: strike price
525+
put_call: option direction. 'CALL' or 'PUT'
526+
multiplier: option multiplier
527+
ask_price:
528+
ask_size:
529+
bid_price:
530+
bid_size:
531+
pre_close:
532+
latest_price:
533+
volume:
534+
open_interest:
535+
"""
536+
params = OptionChainParams()
533537
param = SingleContractParams()
534538
param.symbol = symbol
535539
if isinstance(expiry, str) and re.match('[0-9]{4}-[0-9]{2}-[0-9]{2}', expiry):
536540
param.expiry = int(delorean.parse(expiry, timezone=eastern, dayfirst=False).datetime.timestamp() * 1000)
537541
else:
538542
param.expiry = expiry
539543
params.contracts = [param]
544+
params.option_filter = option_filter if option_filter else OptionFilter(**kwargs)
545+
params.version = OPEN_API_SERVICE_VERSION_V3
540546
request = OpenApiRequest(OPTION_CHAIN, biz_model=params)
541547
response_content = self.__fetch_data(request)
542548
if response_content:

tigeropen/quote/request/model.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,3 +708,36 @@ def to_openapi_dict(self):
708708
if self.market:
709709
params['market'] = self.market
710710
return params
711+
712+
713+
class OptionChainParams(BaseParams):
714+
def __init__(self):
715+
super(OptionChainParams, self).__init__()
716+
self._contracts = None
717+
self._option_filter = None
718+
719+
@property
720+
def contracts(self):
721+
return self._contracts
722+
723+
@contracts.setter
724+
def contracts(self, value):
725+
self._contracts = value
726+
727+
@property
728+
def option_filter(self):
729+
return self._option_filter
730+
731+
@option_filter.setter
732+
def option_filter(self, value):
733+
self._option_filter = value
734+
735+
def to_openapi_dict(self):
736+
params = {'option_basic': list(), 'option_filter': dict()}
737+
738+
if self.contracts:
739+
for contract in self.contracts:
740+
params['option_basic'].append(contract.to_openapi_dict())
741+
if self.option_filter:
742+
params['option_filter'] = self.option_filter.to_dict()
743+
return params

tigeropen/quote/response/option_chains_response.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@
77
import pandas as pd
88

99
from tigeropen.common.response import TigerResponse
10+
from tigeropen.common.util import string_utils
1011

11-
COLUMNS = ['identifier', 'symbol', 'expiry', 'strike', 'put_call', 'multiplier', 'ask_price', 'ask_size', 'bid_price',
12-
'bid_size', 'pre_close', 'latest_price', 'volume', 'open_interest']
13-
CHAIN_FIELD_MAPPINGS = {'askPrice': 'ask_price', 'askSize': 'ask_size', 'bidPrice': 'bid_price', 'bidSize': 'bid_size',
14-
'latestPrice': 'latest_price', 'openInterest': 'open_interest', 'preClose': 'pre_close',
15-
'right': 'put_call'}
12+
CHAIN_FIELD_MAPPINGS = {'right': 'put_call'}
1613

1714

1815
class OptionChainsResponse(TigerResponse):
@@ -25,7 +22,6 @@ def parse_response_content(self, response_content):
2522
response = super(OptionChainsResponse, self).parse_response_content(response_content)
2623
if 'is_success' in response:
2724
self._is_success = response['is_success']
28-
2925
if self.data and isinstance(self.data, list):
3026
chain_data = []
3127
for item in self.data:
@@ -41,8 +37,6 @@ def parse_response_content(self, response_content):
4137
continue
4238
if key == 'right':
4339
value = value.upper()
44-
tag = CHAIN_FIELD_MAPPINGS[key] if key in CHAIN_FIELD_MAPPINGS else key
45-
item_values[tag] = value
46-
chain_data.append([item_values.get(tag) for tag in COLUMNS])
47-
48-
self.chain = pd.DataFrame(chain_data, columns=COLUMNS)
40+
item_values[CHAIN_FIELD_MAPPINGS.get(key, string_utils.camel_to_underline(key))] = value
41+
chain_data.append(item_values)
42+
self.chain = pd.DataFrame(chain_data)

0 commit comments

Comments
 (0)