1+ """Systemathics Ganymede API Token Helpers
2+
3+ This module helps to create tokens to access Systemathics Ganymede authenticated API.
4+
5+ functions:
6+ get_cds_index - Get CDS Index data as a DataFrame using Ganymede gRPC API.
7+ """
8+
9+
10+ import grpc
11+ import pandas as pd
12+ from datetime import date ,datetime
13+ from google .type import date_pb2
14+
15+ from systemathics .apis .type .shared .v1 import asset_pb2 as asset
16+ from systemathics .apis .type .shared .v1 import constraints_pb2 as constraints
17+ from systemathics .apis .type .shared .v1 import date_interval_pb2 as date_interval
18+ import systemathics .apis .type .shared .v1 .identifier_pb2 as identifier
19+ import systemathics .apis .services .daily .v2 .get_daily_pb2 as get_daily
20+ import systemathics .apis .services .daily .v2 .get_daily_pb2_grpc as get_daily_service
21+
22+ import systemathics .apis .helpers .token_helpers as token_helpers
23+ import systemathics .apis .helpers .channel_helpers as channel_helpers
24+
25+ def get_cds_index (ticker , start_date = None , end_date = None , batch = None , selected_fields = None , provider = "Markit" ):
26+ """
27+ Fetch CDS data from gRPC API for a given ticker and date range.
28+
29+ Parameters:
30+ ticker (str): The ticker symbol
31+ start_date (datetime.date or str, optional): Start date for data retrieval.
32+ If None, set not limits
33+ end_date (datetime.date or str, optional): End date for data retrieval.
34+ If None, uses today's date
35+ batch (str, optional): Batch name to be used for filtering. If None, gets all batches.
36+ selected_fields (list, optional): List of specific fields to retrieve. If None, gets all fields.
37+ provider (str): Data provider, default is "Markit"
38+
39+ Returns:
40+ pd.DataFrame: DataFrame with Date as index and all available fields as columns
41+ """
42+
43+ def python_date_to_google_date (py_date ):
44+ """Convert Python date to Google Date protobuf message"""
45+ return date_pb2 .Date (year = py_date .year , month = py_date .month , day = py_date .day )
46+
47+ # Helper function to parse date strings
48+ def parse_date_input (date_input ):
49+ """Convert string dates to date objects if needed."""
50+ if date_input is None :
51+ return None
52+ if isinstance (date_input , date ):
53+ return python_date_to_google_date (date_input )
54+ if isinstance (date_input , datetime ):
55+ return python_date_to_google_date (date_input .date ())
56+ if isinstance (date_input , str ):
57+ d = datetime .strptime (date_input , '%Y-%m-%d' ).date ()
58+ return python_date_to_google_date (d )
59+ raise ValueError (f"Invalid date type: { type (date_input )} " )
60+
61+ # All available fields
62+ all_fields = [
63+ "CompositePriceAsk" , "CompositePriceBid" , "CompositeSpreadAsk" ,
64+ "CompositeSpreadBid" , "ConventionalSpread" , "CreditDv01" ,
65+ "DefaultProbability" , "Heat" , "IrDv01" , "JumpToDefault" ,
66+ "JumpToZero" , "ModelPrice" , "ModelSpread" , "Price" ,
67+ "Rec01" , "RiskyPv01" , "SkewPrice" , "SkewSpread"
68+ ]
69+
70+ # Use all fields if none specified, otherwise validate selected fields
71+ if selected_fields is None :
72+ fields = all_fields
73+ else :
74+ fields = [f for f in selected_fields if f in all_fields ]
75+ if not fields :
76+ raise ValueError ("No valid fields selected" )
77+
78+ # Create identifier
79+ id = identifier .Identifier (
80+ asset_type = asset .AssetType .ASSET_TYPE_CDS_INDEX ,
81+ ticker = ticker
82+ )
83+ id .provider .value = provider
84+
85+ # Build constraints only if we have at least one date
86+ constraints_obj = None
87+ if start_date is not None or end_date is not None :
88+ # Create DateInterval with only the dates that are provided
89+ date_interval_kwargs = {}
90+ if start_date is not None :
91+ date_interval_kwargs ['start_date' ] = parse_date_input (start_date )
92+ if end_date is not None :
93+ date_interval_kwargs ['end_date' ] = parse_date_input (end_date )
94+
95+ constraints_obj = constraints .Constraints (
96+ date_intervals = [date_interval .DateInterval (** date_interval_kwargs )]
97+ )
98+
99+ if batch is None :
100+ # Create request with or without constraints
101+ request_kwargs = {
102+ 'identifier' : id ,
103+ 'fields' : fields
104+ }
105+ if constraints_obj is not None :
106+ request_kwargs ['constraints' ] = constraints_obj
107+
108+ try :
109+ # Open gRPC channel
110+ with channel_helpers .get_grpc_channel () as channel :
111+ # Send request and receive response
112+ token = token_helpers .get_token ()
113+ first = True
114+ response = []
115+ info = None
116+ # Create service stub
117+ service = get_daily_service .DailyServiceStub (channel )
118+
119+
120+ if batch is None :
121+ # Create request with or without constraints
122+ request_kwargs = {
123+ 'identifier' : id ,
124+ 'fields' : fields
125+ }
126+ if constraints_obj is not None :
127+ request_kwargs ['constraints' ] = constraints_obj
128+
129+ vector_request = get_daily .DailyRequest (** request_kwargs )
130+
131+ for data in service .DailyVectorStream (
132+ request = vector_request ,
133+ metadata = [('authorization' , token )]
134+ ):
135+ if first :
136+ info = data
137+ first = False
138+ else :
139+ response .append (data .data )
140+
141+ else :
142+
143+ request_kwargs = {
144+ 'identifier' : id ,
145+ 'fields' : fields ,
146+ 'key' : batch
147+ }
148+ if constraints_obj is not None :
149+ request_kwargs ['constraints' ] = constraints_obj
150+
151+ vector_key_request = get_daily .DailyVectorKeyRequest (** request_kwargs )
152+
153+ for data in service .DailyVectorKeyStream (
154+ request = vector_key_request ,
155+ metadata = [('authorization' , token )]
156+ ):
157+ if first :
158+ info = data
159+ first = False
160+ else :
161+ response .append (data .data )
162+
163+ # Process the response
164+ if not response or info is None :
165+ print ("No data received" )
166+ return pd .DataFrame ()
167+
168+ # Get field indices
169+ available_fields = [f for f in info .info .fields ]
170+ field_indices = {field : available_fields .index (field )
171+ for field in fields if field in available_fields }
172+
173+ # Extract dates
174+ dates = [date (d .date .year , d .date .month , d .date .day ) for d in response ]
175+
176+ # Extract keys
177+ keys = [b .key for b in response ]
178+
179+ # Create dictionary for DataFrame
180+ data_dict = {'Key' : keys }
181+
182+ # Extract data for each field
183+ for field_name , field_index in field_indices .items ():
184+ data_dict [field_name ] = [b .data [field_index ] for b in response ]
185+
186+ # Create DataFrame
187+ df = pd .DataFrame (data_dict , index = dates )
188+ df .index .name = 'Date'
189+
190+ # Sort by date for better readability
191+ df = df .sort_index ()
192+
193+
194+ return df
195+
196+ except grpc .RpcError as e :
197+ print (f"gRPC Error: { e .code ().name } " )
198+ print (f"Details: { e .details ()} " )
199+ return pd .DataFrame ()
200+ except Exception as e :
201+ print (f"Error: { str (e )} " )
202+ return pd .DataFrame ()
0 commit comments