3#include "../../network/oauth.hpp"
7#include <boost/json.hpp>
58static inline std::chrono::system_clock::time_point
parse_expiry(
const char* s) {
78 const std::time_t utc = _mkgmtime(&tm) - 9 * 3600;
80 const std::time_t utc = timegm(&tm) - 9 * 3600;
82 return std::chrono::system_clock::from_time_t(utc);
117 const char*
key = std::getenv(
"KIS_APP_KEY");
118 const char*
secret = std::getenv(
"KIS_APP_SECRET");
120 throw std::runtime_error(
"Add KIS_APP_KEY and KIS_APP_SECRET environment variables.");
141 const std::string& filename
143 std::ifstream ifs(filename);
144 if (!ifs.is_open()) {
145 throw std::runtime_error(
"Cannot open KIS credential file: " + filename);
147 const std::string content(
148 std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>{}
150 boost::system::error_code ec;
151 const auto jv = json::parse(content, ec);
153 throw std::runtime_error(
154 "Failed to parse KIS credentials file: " + filename +
" — " + ec.message()
157 if (!jv.is_object()) {
158 throw std::runtime_error(
"Failed to parse KIS credentials file: " + filename +
" — not a JSON object");
160 const auto& doc = jv.as_object();
161 const json::value*
key = doc.if_contains(
"app_key");
162 const json::value*
secret = doc.if_contains(
"app_secret");
164 throw std::runtime_error(
"Credential file should contain app_key and app_secret as strings");
169 key_ =
key->get_string().c_str();
180 const char* demo = std::getenv(
"KIS_DEMO");
181 if (!demo || std::string_view(demo) ==
"1") {
184 if (std::string_view(demo) ==
"0") {
187 throw std::runtime_error(
188 std::string(
"KIS_DEMO is \"") + demo +
"\"; set it to 1 for the demo endpoints or 0 to trade "
189 "live, or leave it unset for demo."
200 static bool demo_from_json(
const json::object& doc,
const std::string& filename) {
201 const json::value* demo = doc.if_contains(
"demo");
205 if (!demo->is_bool()) {
206 throw std::runtime_error(
207 "KIS credential file " + filename +
" has a \"demo\" that is not true or false; write "
208 "true for the demo endpoints or false to trade live, or leave it out for demo."
211 return demo->get_bool();
218 static constexpr std::string_view
key_field() noexcept {
return "appkey"; }
224 static constexpr std::string_view
secret_field() noexcept {
return "appsecret"; }
231 return is_demo_ ?
"openapivts.koreainvestment.com" :
"openapi.koreainvestment.com";
244 const char*
ws_host() const noexcept {
return "ops.koreainvestment.com"; }
257 return R
"({"grant_type":"client_credentials","appkey":")" + key_ + R"(","secretkey":")" +
277 static constexpr std::string_view
token_field() noexcept {
return "access_token"; }
284 const json::object& doc
294 parse_expiry(doc.at(
"access_token_token_expired").as_string().c_str()) :
324 const json::object& doc
326 if (!doc.contains(
"approval_key")) {
327 throw std::runtime_error(
"WS approval key authentication failed");
339 const auto now = std::chrono::system_clock::now();
340 const bool never_authed =
last_auth_time_ == std::chrono::system_clock::time_point{};
342 std::chrono::duration_cast<std::chrono::seconds>(now -
last_auth_time_).count();
343 return !
approval_key_.empty() && !never_authed && elapsed < 86400;
OAuth client credentials (an app key and secret), the base of each broker's credentials class.
const std::string & key() const noexcept
const std::string & secret() const noexcept
The Korea Investment & Securities (KIS) Open API client.
constexpr const char * market_division_code(MarketTape tape) noexcept
Returns KIS's FID_COND_MRKT_DIV_CODE for a tape.
MarketTape
Which venue's prices KIS market-data requests read (see KisConfig::market_tape_).
@ CONSOLIDATED
KRX and NXT combined (UN).
static std::chrono::system_clock::time_point parse_expiry(const char *s)
Reads KIS's token expiry, YYYY-MM-DD HH:MM:SS in Korean time.
The library: broker-neutral types (core), and the modules below.
KIS credentials (app key and secret), demo or real account, and the market-data tape.
MarketTape market_tape_
Which venue's prices every KIS market-data request reads (quotes, bars, depth, price bands,...
bool is_demo_
Whether to use KIS's demo (모의투자) endpoints; true unless stated otherwise.
static bool demo_from_json(const json::object &doc, const std::string &filename)
Reads a credential file's "demo".
const char * ws_port() const noexcept
Returns the WebSocket port for the environment.
static constexpr std::string_view key_field() noexcept
Returns the token request's field for the app key.
void from_json_impl(const std::string &filename)
Reads the credentials from a JSON file; called by from_json(filename). Nothing changes if it throws.
void from_env_impl()
Reads the credentials from KIS_APP_KEY and KIS_APP_SECRET, and the environment from KIS_DEMO (see dem...
static bool demo_from_env()
Reads KIS_DEMO.
static constexpr std::string_view secret_field() noexcept
Returns the token request's field for the app secret.
std::string ws_to_jsonbody() const
Builds the WebSocket approval-key request body (the secret under secretkey).
const char * rest_port() const noexcept
Returns the REST port for the environment.
const char * rest_host() const noexcept
Returns the REST host for the environment.
const char * ws_host() const noexcept
Returns the WebSocket host (the same for both environments).
void from_json_impl(const json::object &doc)
Reads KIS's stated expiry from a token response; called by from_json.
std::chrono::system_clock::time_point access_token_expired_
KIS's stated expiry (access_token_token_expired), which takes precedence over expires_in.
static constexpr std::string_view token_field() noexcept
Returns the token response's field for the token.
KisRest()
Constructs an empty token.
bool valid() const noexcept
Tests whether the token is set and stays valid for at least five more minutes.
KisWebsocket()
Constructs an empty key.
void from_json(const json::object &doc)
Reads an approval-key response.
bool valid() const noexcept
Tests whether the key is set and was received less than a day ago.
std::chrono::system_clock::time_point last_auth_time_
When it was received.
std::string approval_key_
The approval key.
An OAuth access token and its expiry, the base of each broker's token-response class.
bool valid(std::chrono::seconds margin=std::chrono::minutes(5)) const noexcept
Tests whether the token is set and stays valid for at least a margin.
std::chrono::system_clock::time_point expires_at_