C++ API Usage Examples
These are the basic usage examples of the Otyca C++ API.
1#include <iostream>
2#include <string>
3#include <fstream>
4#include <vector>
5#include <iterator>
6#include <map>
7#include <regex>
8#include <memory>
9
10#include <boost/program_options.hpp>
11#include <boost/date_time/posix_time/posix_time.hpp>
12#include <boost/date_time/local_time/local_time.hpp>
13#include <boost/date_time/gregorian/gregorian.hpp>
14#include <boost/tokenizer.hpp>
15
16#include "underlying_dataset/underlying_dataset.hpp"
17#include "underlying_dataset/option.hpp"
18#include "underlying_dataset/option_expiration.hpp"
19#include "underlying_dataset/option_strike.hpp"
20#include "option_calculator/option_calculator.hpp"
21#include "flexible_schedule.hpp"
22#include "stochastic_process/time_changed_levy_process.hpp"
23#include "yield_curve/piece_wise_yield_curve.hpp"
24#include "option_calculator/realized_variance_tenor_calculator.hpp"
25#include "schedule.hpp"
26#include "stochastic_process/composite_stochastic_process.hpp"
27
28using namespace std;
29using namespace boost::posix_time;
30using namespace boost::gregorian;
31using namespace boost::local_time;
32using namespace otyca;
33
34void parse_underlying_quote_string(const string& input, shared_ptr<UnderlyingDataset> uds) {
35 double bid_price = NAN;
36 double ask_price = NAN;
37 int bid_size = 1;
38 int ask_size = 1;
39
40 smatch match;
41
42 // Parse date and time
43 if (regex_search(input, match, regex("\"Date: ([^\"]+)\""))) {
44 string date_str = match[1]; // e.g. "April 9, 2025 at 11:10 AM EDT"
45
46 // Extract components using regex
47 regex date_components("([A-Za-z]+) ([0-9]+), ([0-9]{4}) at ([0-9]{1,2}):([0-9]{2}) ([AP]M)");
48 smatch date_parts;
49
50 if (regex_search(date_str, date_parts, date_components)) {
51 string month_str = date_parts[1];
52 int day = stoi(date_parts[2]);
53 int year = stoi(date_parts[3]);
54 int hour = stoi(date_parts[4]);
55 int min = stoi(date_parts[5]);
56 string ampm = date_parts[6];
57
58 // Convert month name to number
59 static const map<string, int> months = {
60 {"January", 1}, {"February", 2}, {"March", 3}, {"April", 4},
61 {"May", 5}, {"June", 6}, {"July", 7}, {"August", 8},
62 {"September", 9}, {"October", 10}, {"November", 11}, {"December", 12}
63 };
64
65 int month = months.at(month_str);
66
67 // Adjust for AM/PM
68 if (ampm == "PM" && hour < 12) hour += 12;
69 else if (ampm == "AM" && hour == 12) hour = 0;
70
71 uds->set_valuation_time(ptime(date(year, month, day), time_duration(hour, min, 0)));
72 }
73 }
74
75 // Parse bid/ask prices
76 if (regex_search(input, match, regex("Bid: ([0-9.]+)")))
77 bid_price = stod(match[1]);
78
79 if (regex_search(input, match, regex("Ask: ([0-9.]+)")))
80 ask_price = stod(match[1]);
81
82 // Parse bid/ask sizes
83 if (regex_search(input, match, regex("Size: ([0-9]+)\\*([0-9]+)"))) {
84 bid_size = stoi(match[1]);
85 ask_size = stoi(match[2]);
86 }
87 auto underlying = uds->create_underlying();
88 underlying->set_bid_ask(bid_price, bid_size, ask_price, ask_size);
89}
90
91void parse_option_quote_string(const string& line, shared_ptr<UnderlyingDataset> uds) {
92 typedef boost::tokenizer<boost::escaped_list_separator<char> > Tokenizer;
93
94 Tokenizer tokens(line);
95 vector<string> fields(tokens.begin(), tokens.end());
96
97 if (fields.size() == 22) {
98 istringstream date_ss(fields[0]);
99 tm tm = {};
100 date_ss >> get_time(&tm, "%a %b %d %Y");
101 date expiration_date(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
102 auto expiration = ptime(expiration_date, time_duration(16, 0, 0));
103
104 double strike = stod(fields[11]);
105
106 double call_bid = stod(fields[4]);
107 double call_ask = stod(fields[5]);
108
109 double put_bid = stod(fields[15]);
110 double put_ask = stod(fields[16]);
111
112 auto call = uds->create_option(expiration, strike, CALL);
113 call->set_bid_ask(call_bid, 1, call_ask, 1); // the example data doesn't have sizes
114
115 auto put = uds->create_option(expiration, strike, PUT);
116 put->set_bid_ask(put_bid, 1, put_ask, 1); // the example data doesn't have sizes
117 }
118}
119
120void read_cboe_option_chain(const string& filename, shared_ptr<UnderlyingDataset> uds) {
121 ifstream file(filename);
122 if (file.is_open()) {
123 string line;
124 //skip empty line
125 getline(file, line);
126
127 //skip last price line
128 getline(file, line);
129
130 //parse underlying bid ask prices
131 getline(file, line);
132 parse_underlying_quote_string(line, uds);
133
134 //skip option header
135 getline(file, line);
136
137 //parse option bid ask prices
138 while (getline(file, line)) {
139 parse_option_quote_string(line, uds);
140 }
141
142 file.close();
143 }
144}
145
146void read_csv(const string& filename, function<void(const vector<string>&)> handler) {
147 ifstream file(filename);
148 string line;
149 while (getline(file, line)) {
150 if (line.empty() || line[0] == '#') continue;
151
152 stringstream ss(line);
153 vector<string> t;
154 for (string item; getline(ss, item, ','); ) t.push_back(item);
155
156 handler(t);
157 }
158}
159
160void read_dividends(const string& filename, shared_ptr<UnderlyingDataset> uds) {
161 read_csv(filename, [uds](const vector<string>& t) {
162 uds->add_projected_dividend(from_string(t[0]), stod(t[2]), stod(t[3]), stoi(t[1]));
163 });
164}
165
166void read_yield_curve(const string& filename, shared_ptr<UnderlyingDataset> uds) {
167 auto yc = make_shared<PieceWiseYieldCurve>();
168 read_csv(filename, [yc](const vector<string>& t) {
169 yc->add(stod(t[0]), Estimate(stod(t[1]), stod(t[2])));
170 });
171 uds->set_yield_curve(yc);
172}
173
174/*
175 * run it like this:
176 * volatility_surface_calibration_example --quotedata spy_quotedata.csv --dividends spy_dividends.csv --yieldcurve yieldcurve.csv
177 */
178int main(int argc, char* argv[]) {
179 try {
180 string quotedata_file, dividends_file, yieldcurve_file;
181 boost::program_options::options_description desc("Allowed options");
182 desc.add_options()
183 ("help,h", "produce help message")
184 ("quotedata",
185 boost::program_options::value<string>("edata_file)->required(),
186 "file contains underlying and option quotes")
187 ("dividends",
188 boost::program_options::value<string>(÷nds_file),
189 "file that contains dividends (optional)")
190 ("yieldcurve",
191 boost::program_options::value<string>(&yieldcurve_file),
192 "file that contains discount rates and uncertainties (optional)")
193 ;
194 boost::program_options::variables_map vm;
195 boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
196 if (vm.count("help")) {
197 cout << desc << "\n";
198 return 0;
199 }
200 boost::program_options::notify(vm);
201
202 // Optionally set log call back and level threshold
203 Logger::set_log_callback([](LogLevel level, const string& message) {
204 cout << "[" << Logger::level_to_string(level) << "] " << message << endl;
205 });
206 Logger::set_log_level(LogLevel::Debug);
207
208 // UnderlyingDataset has to be instantiated by make_shared
209 auto uds = make_shared<UnderlyingDataset>();
210
211 // overwrite if needed
212 // auto tc = make_shared<RealizedVarianceTenorCalculator>();
213 // tc->set_variance_weights(0.98, 0.01, 0.01);
214 // ud.set_variance_tenor_calculator(tc);
215 // uds->set_option_style(AMERICAN);
216 // uds.set_option_settlement_lag(1);
217 // uds.set_underlying_settlement_lag(1);
218 // uds.set_exercise_settlement_lag(1);
219
220 // set underlying and option prices
221 read_cboe_option_chain(quotedata_file, uds);
222
223 // dividends are optional
224 if (vm.count("dividends"))
225 read_dividends(dividends_file, uds);
226
227 // yield curve will be implied if not provided
228 if (vm.count("yieldcurve"))
229 read_yield_curve(yieldcurve_file, uds);
230
231 /*
232 * add a stochastic jump diffusion model which consists of Heston diffusion (LCIR) and Merton (MT) jump.
233 * add upto 3 Bimodal Gaussian jumps and calibrate the process using all options with tenor in the range of
234 * (0, 2] years
235 */
236 // uds->add_composite_stochastic_process_config({"LCIR(MT)", 3, 0, 2});
237
238 /*
239 * add a stochastic jump diffusion model which consists of Heston diffusion (LCIR) and double exponential (KOU)
240 * jump. add upto 1 Bimodal Gaussian jump and calibrate the process using all options with tenor in the range of
241 * (0, 0.5] years
242 */
243 // uds->add_composite_stochastic_process_config({"LCIR(KOU)", 1, 0, 0.5});
244
245 // after all the inputs are setup, call setup()
246 uds->setup();
247
248 // now call various getters to retrieve calculated results
249
250 // get the 3 month and 1 year atm volatility evaluated by each calibrated stochastic jump diffusion model
251 for (const auto& p : uds->get_composite_stochastic_processes()) {
252 cout<<p->name() << " 3 month atm vol: " << p->volatility(0.25, 0) << endl;
253 cout<<p->name() << " 1 year atm vol: " << p->volatility(1.0, 0) << endl;
254 }
255
256 auto implied_event_schedule = uds->get_implied_event_schedule();
257 cout << "implied event schedule" << endl << *implied_event_schedule << endl;
258
259 if (shared_ptr<Schedule<Dividend> > div_schedule = uds->get_dividend_schedule())
260 cout << "dividend schedule" << endl << *div_schedule << endl;
261
262 // now we want to retrieve expiration by expiration quantities
263 cout << "expiration,forward_value,forward_uncertainty,implied_interest_value,implied_interest_uncertainty,";
264 cout << "implied_borrow_value,implied_borrow_uncertainty,implied_forward_value,implied_forward_uncertainty,";
265 cout << "variance_term_structure_model_value,fair_atm_volatility,atm_volatility_value,fair_volatility_skew,";
266 cout << "vix_style_volatility,atm_volatility_uncertainty,";
267 cout << endl;
268 for (const auto& option_expiration: uds->get_option_expirations()) {
269 cout << option_expiration->get_expiration() << ",";
270 cout << option_expiration->get_forward_value() << ",";
271 cout << option_expiration->get_forward_uncertainty() << ",";
272 cout << option_expiration->get_implied_interest_value() << ",";
273 cout << option_expiration->get_implied_interest_uncertainty() << ",";
274 cout << option_expiration->get_implied_borrow_value() << ",";
275 cout << option_expiration->get_implied_borrow_uncertainty() << ",";
276 cout << option_expiration->get_implied_forward_value() << ",";
277 cout << option_expiration->get_implied_forward_uncertainty() << ",";
278 cout << option_expiration->get_variance_term_structure_model_value() << ",";
279 cout << option_expiration->get_fair_atm_volatility() << ",";
280 cout << option_expiration->get_atm_volatility_value() << ",";
281 cout << option_expiration->get_fair_volatility_skew() << ",";
282 cout << option_expiration->get_vix_style_volatility() << ",";
283 cout << option_expiration->get_atm_volatility_uncertainty() << ",";
284 cout << endl;
285 }
286
287 // now we want to retrieve strike by strike quantities for each expiration
288 cout << "expiration,strike,call_bid_volatility,call_ask_volatility,put_bid_volatility,put_ask_volatility,";
289 cout << "strike_volatility_value,strike_volatility_uncertainty,fair_volatility," << endl;
290 for (const auto& option_expiration: uds->get_option_expirations()) {
291 for (const auto& option_strike: option_expiration->get_option_strikes()) {
292 auto call = option_strike->get_call();
293 auto put = option_strike->get_put();
294
295 cout << option_expiration->get_expiration() << ','
296 << option_strike->get_strike() << ','
297 << call->get_bid_volatility() << ','
298 << call->get_ask_volatility() << ','
299 << put->get_bid_volatility() << ','
300 << put->get_ask_volatility() << ','
301 << option_strike->get_strike_volatility_value() << ','
302 << option_strike->get_strike_volatility_uncertainty() << ','
303 << option_strike->get_fair_volatility() << ','
304 << '\n';
305 }
306 }
307 }
308 catch (const boost::program_options::error& e) {
309 cerr << "Error: " << e.what() << "\n";
310 return 1;
311 }
312}
Preview of spy_quotedata.csv
SPDR S&P 500 ETF Trust,Last: 564.4,Change: 3.38
"Date: March 19, 2025 at 10:57 AM EDT",Bid: 564.39,Ask: 564.4,Size: 1*1,"Volume: 6,805,714"
Expiration Date,Calls,Last Sale,Net,Bid,Ask,Volume,IV,Delta,Gamma,Open Interest,Strike,Puts,Last Sale,Net,Bid,Ask,Volume,IV,Delta,Gamma,Open Interest
Wed Mar 19 2025,SPY250319C00400000,161.33,0.795,163.15,165,4,5.5771,0.9997,0,132,400.00,SPY250319P00400000,0.01,0,0,0.01,0,4.003,-0.0003,0,752
Wed Mar 19 2025,SPY250319C00405000,153.27,0,158.16,160.9,0,5.322,0.9997,0,8,405.00,SPY250319P00405000,0.01,0.005,0,0.01,2,3.8669,-0.0003,0,5
Wed Mar 19 2025,SPY250319C00410000,147.83,0,153.18,155.86,0,5.8506,0.9997,0,8,410.00,SPY250319P00410000,0.01,0,0,0.01,0,3.7323,-0.0003,0,39
Wed Mar 19 2025,SPY250319C00415000,0,0,148.17,150.81,0,5.9167,0.9997,0,0,415.00,SPY250319P00415000,0.01,0,0,0.01,0,3.5992,-0.0003,0,7
Wed Mar 19 2025,SPY250319C00420000,139.34,0,143.2,146.12,0,5.0644,0.9996,0,4,420.00,SPY250319P00420000,0.02,0,0,0.01,0,3.4674,-0.0004,0,13225
Wed Mar 19 2025,SPY250319C00425000,0,0,138.17,140.93,0,0,0.9996,0,0,425.00,SPY250319P00425000,0.01,0,0,0.01,0,3.3368,-0.0004,0,2256
Preview of spy_dividends.csv
#assuming annualized yield of 1.2%, fade from pure cash to pure proportional in 1.5 years
#yyyy-mm-dd,uncertainty-in-days,fixed-cash,proportional
2025-03-21,0,1.6932,0
2025-06-20,0,1.4110,0.0005
2025-09-19,0,1.1288,0.0010
2025-12-19,0,0.8466,0.0015
2026-03-20,0,0.5644,0.0020
2026-06-19,0,0.2822,0.0025
2026-09-18,0,0,0.003
2026-12-18,0,0,0.003
Preview of yieldcurve.csv
#tenor,rate,rate-uncertainty
0.219,0.04544,0.005
0.526,0.04424,0.004
1.025,0.04289,0.003
2.770,0.04109,0.003