Ankit3445/options_pricing
0
1#include "AlphaVantageClient.hpp"2 3#include <iostream>4#include <sstream>5#include <cmath>6#include <thread>7#include <chrono>8#include <random>9#include <algorithm>10 11#include <boost/beast/core.hpp>12#include <boost/beast/http.hpp>13#include <boost/beast/ssl.hpp>14#include <boost/beast/version.hpp>15#include <boost/asio/connect.hpp>16#include <boost/asio/ip/tcp.hpp>17#include <boost/asio/ssl/error.hpp>18#include <boost/asio/ssl/stream.hpp>19#include <openssl/ssl.h>20#include <nlohmann/json.hpp>21 22namespace beast = boost::beast;23namespace http = beast::http;24namespace net = boost::asio;25namespace ssl = net::ssl;26using tcp = net::ip::tcp;27 28AlphaVantageClient::AlphaVantageClient(29 const std::vector<std::string>& symbols,30 const std::string& api_key,31 const AppConfig& config,32 std::shared_ptr<ThreadSafeQueue<MarketTick>> queue)33 : symbols_(symbols)34 , api_key_(api_key)35 , config_(config)36 , queue_(std::move(queue))37{38}39 40AlphaVantageClient::~AlphaVantageClient() {41 stop();42}43 44void AlphaVantageClient::start() {45 if (running_.exchange(true)) return;46 thread_ = std::make_unique<std::thread>([this] { polling_loop(); });47}48 49void AlphaVantageClient::stop() {50 running_.store(false);51 if (thread_ && thread_->joinable()) {52 thread_->join();53 }54}55 56void AlphaVantageClient::pause() {57 paused_.store(true);58 std::cout << "[AlphaVantage] Paused" << std::endl;59}60 61void AlphaVantageClient::resume() {62 paused_.store(false);63 std::cout << "[AlphaVantage] Resumed" << std::endl;64}65 66bool AlphaVantageClient::is_paused() const {67 return paused_.load();68}69 70void AlphaVantageClient::request_reset() {71 reset_requested_.store(true);72 paused_.store(false);73 std::cout << "[AlphaVantage] Reset requested" << std::endl;74}75 76void AlphaVantageClient::set_symbols(const std::vector<std::string>& symbols) {77 std::lock_guard<std::mutex> lock(symbols_mutex_);78 symbols_ = symbols;79 symbol_index_.store(0);80 std::cout << "[AlphaVantage] Symbols updated:";81 for (const auto& s : symbols_) std::cout << " " << s;82 std::cout << std::endl;83}84 85std::vector<std::string> AlphaVantageClient::get_symbols() const {86 std::lock_guard<std::mutex> lock(symbols_mutex_);87 return symbols_;88}89 90void AlphaVantageClient::polling_loop() {91 while (running_.load()) {92 if (paused_.load()) {93 std::this_thread::sleep_for(std::chrono::milliseconds(200));94 continue;95 }96 97 if (reset_requested_.exchange(false)) {98 queue_->clear();99 symbol_index_.store(0);100 std::cout << "[AlphaVantage] Queue cleared after reset" << std::endl;101 }102 103 std::string symbol;104 {105 std::lock_guard<std::mutex> lock(symbols_mutex_);106 if (symbols_.empty()) {107 std::this_thread::sleep_for(std::chrono::seconds(1));108 continue;109 }110 symbol = symbols_[symbol_index_.fetch_add(1) % symbols_.size()];111 }112 113 try {114 MarketTick base = fetch_quote(symbol);115 std::vector<MarketTick> chain = expand_option_chain(base);116 117 for (auto& tick : chain) {118 queue_->push(std::move(tick));119 }120 121 std::cout << "[AlphaVantage] " << symbol122 << " spot=" << base.spotPrice123 << " -> " << chain.size() << " options queued"124 << std::endl;125 } catch (const std::exception& ex) {126 std::cerr << "[AlphaVantage] " << symbol << " error: " << ex.what() << std::endl;127 }128 129 for (int i = 0; i < static_cast<int>(config_.rate_limit_sec) && running_.load(); ++i) {130 if (paused_.load() || reset_requested_.load()) break;131 std::this_thread::sleep_for(std::chrono::seconds(1));132 }133 }134}135 136std::vector<MarketTick> AlphaVantageClient::expand_option_chain(const MarketTick& base) {137 std::vector<MarketTick> chain;138 chain.reserve(config_.strike_pct.size() * config_.expiries.size() * ((config_.use_calls ? 1 : 0) + (config_.use_puts ? 1 : 0)));139 140 for (double strike_pct : config_.strike_pct) {141 double strike = std::round(base.spotPrice * strike_pct);142 143 for (double expiry : config_.expiries) {144 if (config_.use_calls) {145 MarketTick tick = base;146 tick.strikePrice = strike;147 tick.timeToExpiry = expiry;148 tick.optionType = OptionType::CALL;149 tick.optionMarketPrice = compute_option_market_price(150 base.spotPrice, strike, expiry, base.riskFreeRate, OptionType::CALL);151 chain.push_back(std::move(tick));152 }153 154 if (config_.use_puts) {155 MarketTick tick = base;156 tick.strikePrice = strike;157 tick.timeToExpiry = expiry;158 tick.optionType = OptionType::PUT;159 tick.optionMarketPrice = compute_option_market_price(160 base.spotPrice, strike, expiry, base.riskFreeRate, OptionType::PUT);161 chain.push_back(std::move(tick));162 }163 }164 }165 166 return chain;167}168 169MarketTick AlphaVantageClient::fetch_quote(const std::string& symbol) {170 std::string host = "www.alphavantage.co";171 std::string port = "443";172 std::string target = "/query?function=GLOBAL_QUOTE&symbol=" + symbol + "&apikey=" + api_key_;173 174 net::io_context ioc;175 ssl::context ctx(ssl::context::tls_client);176 ctx.set_default_verify_paths();177 ctx.set_verify_mode(ssl::verify_none);178 ctx.set_options(179 ssl::context::default_workarounds |180 ssl::context::no_sslv2 |181 ssl::context::no_sslv3 |182 ssl::context::single_dh_use183 );184 185 beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);186 beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30));187 188 tcp::resolver resolver(ioc);189 auto const results = resolver.resolve(host, port);190 beast::get_lowest_layer(stream).connect(results);191 192 if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {193 throw std::runtime_error("SSL SNI setup failed");194 }195 196 stream.handshake(ssl::stream_base::client);197 198 http::request<http::string_body> req{http::verb::get, target, 11};199 req.set(http::field::host, host);200 req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);201 http::write(stream, req);202 203 beast::flat_buffer buffer;204 http::response<http::dynamic_body> res;205 http::read(stream, buffer, res);206 207 beast::error_code ec;208 stream.shutdown(ec);209 210 auto body = beast::buffers_to_string(res.body().data());211 auto json = nlohmann::json::parse(body);212 213 std::string price_str = "0.0";214 if (json.contains("Global Quote") && json["Global Quote"].contains("05. price")) {215 price_str = json["Global Quote"]["05. price"].get<std::string>();216 } else if (json.contains("Note")) {217 std::cerr << "[AlphaVantage] Rate-limit note: " << json["Note"] << std::endl;218 } else if (json.contains("Information")) {219 std::cerr << "[AlphaVantage] Info: " << json["Information"] << std::endl;220 }221 222 double spot = 0.0;223 try {224 spot = std::stod(price_str);225 } catch (...) {226 spot = 0.0;227 }228 if (spot <= 0.0) {229 spot = synthetic_spot(symbol);230 static thread_local std::mt19937_64 noise_rng(std::random_device{}());231 std::normal_distribution<double> noise(0.0, spot * 0.01);232 spot += noise(noise_rng);233 spot = std::max(spot, 1.0);234 std::cout << "[AlphaVantage] " << symbol << " using synthetic spot $" << spot << std::endl;235 }236 237 auto now = std::chrono::system_clock::now();238 auto tt = std::chrono::system_clock::to_time_t(now);239 std::stringstream ss;240 ss << std::put_time(std::localtime(&tt), "%Y-%m-%d %H:%M:%S");241 std::string timestamp = ss.str();242 243 MarketTick tick;244 tick.ticker = symbol;245 tick.timestamp = timestamp;246 tick.spotPrice = spot;247 tick.strikePrice = std::round(spot);248 tick.timeToExpiry = 0.25;249 tick.riskFreeRate = 0.04;250 tick.optionType = OptionType::CALL;251 tick.optionMarketPrice = compute_option_market_price(spot, tick.strikePrice, tick.timeToExpiry, tick.riskFreeRate, OptionType::CALL);252 253 return tick;254}255 256double AlphaVantageClient::synthetic_spot(const std::string& symbol) {257 std::hash<std::string> hasher;258 size_t h = hasher(symbol);259 std::seed_seq seed{static_cast<int>(h & 0xffffffff), static_cast<int>((h >> 32) & 0xffffffff)};260 std::mt19937_64 local_rng(seed);261 std::uniform_real_distribution<double> dist(20.0, 500.0);262 return std::round(dist(local_rng) * 100.0) / 100.0;263}264 265double AlphaVantageClient::compute_option_market_price(266 double spot, double strike, double T, double r, OptionType type) const267{268 double sigma = 0.25;269 double d1 = (std::log(spot / strike) + (r + sigma * sigma * 0.5) * T)270 / (sigma * std::sqrt(T));271 double d2 = d1 - sigma * std::sqrt(T);272 double nd1 = 0.5 * (1.0 + std::erf( d1 / std::sqrt(2.0)));273 double nd2 = 0.5 * (1.0 + std::erf( d2 / std::sqrt(2.0)));274 double n_nd1 = 0.5 * (1.0 + std::erf(-d1 / std::sqrt(2.0)));275 double n_nd2 = 0.5 * (1.0 + std::erf(-d2 / std::sqrt(2.0)));276 277 double price;278 if (type == OptionType::CALL) {279 price = spot * nd1 - strike * std::exp(-r * T) * nd2;280 } else {281 price = strike * std::exp(-r * T) * n_nd2 - spot * n_nd1;282 }283 284 static thread_local std::mt19937_64 rng(std::random_device{}());285 static thread_local std::normal_distribution<double> noise(0.0, 1.0);286 price *= (1.0 + noise(rng) * 0.03);287 price = std::max(price, 0.01);288 289 return price;290}291 