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>(&quotedata_file)->required(),
186                    "file contains underlying and option quotes")
187                ("dividends",
188                    boost::program_options::value<string>(&dividends_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

Download full CSV

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

Download full CSV

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

Download full CSV