-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmsBBGhistoryLink.cpp
368 lines (329 loc) · 11.6 KB
/
msBBGhistoryLink.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/* To launch this program from within Mathematica use:
* In[1]:= link = Install["msBBGhistoryLink"]
*
* Or, launch this program from a shell and establish a
* peer-to-peer connection. When given the prompt Create Link:
* type a port name. ( On Unix platforms, a port name is a
* number less than 65536. On Mac or Windows platforms,
* it's an arbitrary word.)
* Then, from within Mathematica use:
* In[1]:= link = Install["portname", LinkMode->Connect]
*/
#include "wstp.h"
#include <blpapi_defs.h>
#include <blpapi_event.h>
#include <blpapi_element.h>
#include <blpapi_eventdispatcher.h>
#include <blpapi_exception.h>
#include <blpapi_logging.h>
#include <blpapi_message.h>
#include <blpapi_name.h>
#include <blpapi_request.h>
#include <blpapi_session.h>
#include <blpapi_subscriptionlist.h> // correlationID should be in here
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stdlib.h>
#include <string.h>
using namespace BloombergLP;
using namespace blpapi;
using namespace std;
namespace {
const Name SECURITY_DATA("securityData");
const Name SECURITY("security");
const Name FIELD_DATA("fieldData");
const Name SEQ_NUMBER("sequenceNumber");
const Name RESPONSE_ERROR("responseError");
const Name SECURITY_ERROR("securityError");
const Name FIELD_EXCEPTIONS("fieldExceptions");
const Name FIELD_ID("fieldId");
const Name ERROR_INFO("errorInfo");
const Name CATEGORY("category");
const Name MESSAGE("message");
const Name REASON("reason");
const Name SESSION_TERMINATED("SessionTerminated");
const Name SESSION_STARTUP_FAILURE("SessionStartupFailure");
};
extern "C" {
void loggingCallback(blpapi_UInt64_t threadId,
int severity,
blpapi_Datetime_t timestamp,
const char *category,
const char *message);
}
void loggingCallback(blpapi_UInt64_t threadId,
int severity,
blpapi_Datetime_t timestamp,
const char *category,
const char *message)
{
std::stringstream outstream;
std::string severityString;
switch (severity) {
// The following cases will not happen if callback registered at OFF
case blpapi_Logging_SEVERITY_FATAL:
{
severityString = "FATAL";
} break;
// The following cases will not happen if callback registered at FATAL
case blpapi_Logging_SEVERITY_ERROR:
{
severityString = "ERROR";
} break;
// The following cases will not happen if callback registered at ERROR
case blpapi_Logging_SEVERITY_WARN:
{
severityString = "WARN";
} break;
// The following cases will not happen if callback registered at WARN
case blpapi_Logging_SEVERITY_INFO:
{
severityString = "INFO";
} break;
// The following cases will not happen if callback registered at INFO
case blpapi_Logging_SEVERITY_DEBUG:
{
severityString = "DEBUG";
} break;
// The following case will not happen if callback registered at DEBUG
case blpapi_Logging_SEVERITY_TRACE:
{
severityString = "TRACE";
} break;
};
std::stringstream sstream;
sstream << category << " [" << severityString << "] Thread ID = "
<< threadId << ": " << message << std::endl;
outstream << sstream.str() << std::endl;;
// return outstream.str();
}
class HistoryObject
{
std::string d_host;
int d_port;
std::vector<std::string> d_securities;
std::vector<std::string> d_fields;
bool bloombergLists(const char* ticker, const char* field) // formerly bool parseCommandLine(int argc, char **argv) from HistoryObject.cpp
{
int verbosityCount = 0;
// for the moment assumes a single ticker and single field
d_securities.push_back(ticker);
d_fields.push_back(field);
/* for (int i = 1; i < argc; ++i) {
if (!std::strcmp(argv[i], "-s") && i + 1 < argc) {
d_securities.push_back(argv[++i]);
}
else if (!std::strcmp(argv[i], "-f") && i + 1 < argc) {
d_fields.push_back(argv[++i]);
}
else if (!std::strcmp(argv[i], "-ip") && i + 1 < argc) {
d_host = argv[++i];
}
else if (!std::strcmp(argv[i], "-p") && i + 1 < argc) {
d_port = std::atoi(argv[++i]);
}
else if (!std::strcmp(argv[i], "-v")) {
++verbosityCount;
}
else {
printUsage();
return false;
}
} */
if (verbosityCount) {
registerCallback(verbosityCount);
}
// handle default arguments
if (d_securities.size() == 0) {
d_securities.push_back("IBM US Equity");
}
if (d_fields.size() == 0) {
d_fields.push_back("PX_LAST");
}
return true;
}
std::string printErrorInfo(const char *leadingStr, const Element &errorInfo)
{
std::stringstream outstream;
outstream << leadingStr
<< errorInfo.getElementAsString(CATEGORY)
<< " ("
<< errorInfo.getElementAsString(MESSAGE)
<< ")" << std::endl;
return outstream.str();
}
void registerCallback(int verbosityCount)
{
blpapi_Logging_Severity_t severity = blpapi_Logging_SEVERITY_OFF;
switch (verbosityCount) {
case 1: {
severity = blpapi_Logging_SEVERITY_INFO;
}break;
case 2: {
severity = blpapi_Logging_SEVERITY_DEBUG;
}break;
default: {
severity = blpapi_Logging_SEVERITY_TRACE;
}
};
blpapi_Logging_registerCallback(loggingCallback, severity);
}
// per the Bloomberg documentation, a messageIterator can have multiple messages, each of which is also an element and can have sub-elements. Each element can be an array.
// securityData is typically the most interesting element, and it is always (?) an array.
std::string sendRefDataRequest(Session &session, const char* startDate, const char* endDate, const char* periodicitySelection, const char* periodicityAdjustment, int useDPDF, int debug)
{
std::stringstream outstream;
Service refDataService = session.getService("//blp/refdata");
Request request = refDataService.createRequest("HistoricalDataRequest");
// Add securities to request
Element securities = request.getElement("securities");
for (size_t i = 0; i < d_securities.size(); ++i) {
securities.appendValue(d_securities[i].c_str());
}
// Add fields to request
Element fields = request.getElement("fields");
for (size_t i = 0; i < d_fields.size(); ++i) {
fields.appendValue(d_fields[i].c_str());
}
request.set("periodicityAdjustment", periodicityAdjustment);
request.set("periodicitySelection", periodicitySelection);
request.set("startDate", startDate);
request.set("endDate", endDate);
// request.set("returnNullValue", "True"); // this is useful for handling aberrant cases in msGetBBG(), not sure if it's needed here.
if (useDPDF == 0)
{
request.set("adjustmentFollowDPDF", false); }
else
{
request.set("adjustmentFollowDPDF", true); }
outstream << "Sending Request: " << request << std::endl;
session.sendRequest(request);
return outstream.str();
}
// return true if processing is completed, false otherwise
std::string processResponseEvent(Event event, int debug)
{
std::stringstream outstream;
MessageIterator msgIter(event);
while (msgIter.next()) {
Message msg = msgIter.message();
Element securities = msg.asElement();
if (securities.numElements()>0) {
Element securityData = msg.getElement(SECURITY_DATA); //get security
Element fieldDataArray = securityData.getElement(FIELD_DATA);
outstream << "{";
for (size_t j = 0; j <fieldDataArray.numValues(); ++j) {
Element fieldData = fieldDataArray.getValueAsElement(j); //get each element in the security, every single data point (date, data) is an element
for (size_t k = 0; k < fieldData.numElements(); k = k + 2) {
outstream << "{\"";
Element field1 = fieldData.getElement(k); //date is an element
Element field2 = fieldData.getElement(k + 1); //data is an element
outstream << field1.getValueAsString();
outstream << "\", ";
outstream << field2.getValueAsString();
outstream << "}";
}
if (j < fieldDataArray.numValues() - 1)
outstream << ",";
}
outstream << "}";
}
}
return outstream.str();
}
std::string eventLoop(Session &session, int debug)
{
std::stringstream outstream;
bool done = false;
while (!done) {
Event event = session.nextEvent();
if (event.eventType() == Event::PARTIAL_RESPONSE) {
if (debug > 0) {
outstream << "Processing Partial Response" << std::endl;
}
outstream << processResponseEvent(event, debug);
}
else if (event.eventType() == Event::RESPONSE) {
if (debug > 0) {
outstream << "Processing Response" << std::endl;
}
outstream << processResponseEvent(event, debug);
done = true;
}
else {
MessageIterator msgIter(event);
while (msgIter.next()) {
Message msg = msgIter.message();
if (event.eventType() == Event::REQUEST_STATUS) {
outstream << "REQUEST FAILED: " << msg.getElement(REASON) << std::endl;
done = true;
}
else if (event.eventType() == Event::SESSION_STATUS) {
if (msg.messageType() == SESSION_TERMINATED ||
msg.messageType() == SESSION_STARTUP_FAILURE) {
outstream << "session terminated";
done = true;
}
}
}
}
}
return outstream.str();
}
public:
HistoryObject()
{
d_host = "localhost";
d_port = 8194;
}
~HistoryObject()
{
}
// makes one or more Bloomberg calls, gets the Event response and associated MessageIterator, then calls ProcessMessage() to deal with what it's gotten. Returns the result to msGetBBG()
// adapted from BBG 2016, closely related to the "Retrieve" function in the 2008/2009 code
std::string sendHistoryRequest(const char* ticker, const char* field, const char* startDate, const char* endDate, const char* periodicitySelection, const char* periodicityAdjustment, int useDPDF, int debug)
{
std::stringstream outstream;
bloombergLists(ticker, field); /* sets up tickers and fields */
SessionOptions sessionOptions;
sessionOptions.setServerHost(d_host.c_str());
sessionOptions.setServerPort(d_port);
if (debug > 0) { outstream << "Connecting to " + d_host + ":" << d_port << std::endl; }
Session session(sessionOptions);
if (!session.start()) {
outstream << "Failed to start session." << std::endl;
return outstream.str();
}
if (!session.openService("//blp/refdata")) {
outstream << "Failed to open //blp/refdata" << std::endl;
return outstream.str();
}
sendRefDataRequest(session, startDate, endDate, periodicitySelection, periodicityAdjustment, useDPDF, debug);
// wait for events from session.
try {
outstream << eventLoop(session, debug);
}
catch (Exception &e) {
outstream << "Library Exception !!!"
<< e.description()
<< std::endl;
}
catch (...) {
outstream << "Unknown Exception !!!" << std::endl;
}
session.stop();
return outstream.str();
}
};
// This is our entry point. It's called by WSTP template code.
// Per the template file this has a returntype of Manual, which means I need to use a function like WSPutInteger32() to return an integer or WSPutString() to return a string.
void msBBGhistoryLink(const char* ticker, const char* field, const char* startDate, const char* endDate, const char* periodicitySelection = "DAILY", const char* periodicityAdjustment = "Actual", int useDPDF=0, int debug=0)
{
std::string bbgOutput;
HistoryObject bbgHistObject;
bbgOutput = bbgHistObject.sendHistoryRequest(ticker, field, startDate, endDate, periodicitySelection, periodicityAdjustment, useDPDF, debug);
WSPutString(stdlink, bbgOutput.c_str()/* std::string converted to const char */); // https://reference.wolfram.com/language/ref/c/WSPutString.html
return;
}