3#include "../../network/oauth.hpp"
5#include "../../core/types.hpp"
7#include <boost/json.hpp>
66 if (text.size() != 14) {
69 for (
const char digit : text) {
70 if (digit <
'0' || digit >
'9') {
74 const auto number = [text](std::size_t offset, std::size_t length) {
76 for (std::size_t index = offset; index < offset + length; ++index) {
77 value = value * 10 + (text[index] -
'0');
82 parts.tm_year = number(0, 4) - 1900;
83 parts.tm_mon = number(4, 2) - 1;
84 parts.tm_mday = number(6, 2);
85 parts.tm_hour = number(8, 2);
86 parts.tm_min = number(10, 2);
87 parts.tm_sec = number(12, 2);
90 const std::time_t utc = _mkgmtime(&parts) - 9 * 3600;
92 const std::time_t utc = timegm(&parts) - 9 * 3600;
94 return std::chrono::system_clock::from_time_t(utc);
210 const char*
key = std::getenv(
"KIWOOM_APP_KEY");
211 const char*
secret = std::getenv(
"KIWOOM_SECRET_KEY");
213 throw std::runtime_error(
"Add KIWOOM_APP_KEY and KIWOOM_SECRET_KEY environment variables.");
234 std::ifstream ifs(filename);
235 if (!ifs.is_open()) {
236 throw std::runtime_error(
"Cannot open Kiwoom credential file: " + filename);
238 const std::string content(
239 std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>{}
241 boost::system::error_code ec;
242 const auto jv = json::parse(content, ec);
243 if (ec || !jv.is_object()) {
244 throw std::runtime_error(
245 "Failed to parse Kiwoom credentials file: " + filename +
246 (ec ?
" — " + ec.message() : std::string(
" — not a JSON object"))
249 const auto& doc = jv.as_object();
250 const json::value*
key = doc.if_contains(
"appkey");
251 const json::value*
secret = doc.if_contains(
"secretkey");
253 throw std::runtime_error(
"Credential file should contain appkey and secretkey as strings");
258 key_ =
key->get_string().c_str();
269 const char* demo = std::getenv(
"KIWOOM_DEMO");
270 if (!demo || std::string_view(demo) ==
"1") {
273 if (std::string_view(demo) ==
"0") {
276 throw std::runtime_error(
277 std::string(
"KIWOOM_DEMO is \"") + demo +
"\"; set it to 1 for the demo endpoints or 0 to trade "
278 "live, or leave it unset for demo."
289 static bool demo_from_json(
const json::object& doc,
const std::string& filename) {
290 const json::value* demo = doc.if_contains(
"demo");
294 if (!demo->is_bool()) {
295 throw std::runtime_error(
296 "Kiwoom credential file " + filename +
" has a \"demo\" that is not true or false; write "
297 "true for the demo endpoints or false to trade live, or leave it out for demo."
300 return demo->get_bool();
307 static constexpr std::string_view
key_field() noexcept {
return "appkey"; }
313 static constexpr std::string_view
secret_field() noexcept {
return "secretkey"; }
320 return is_demo_ ?
"mockapi.kiwoom.com" :
"api.kiwoom.com";
327 const char*
rest_port() const noexcept {
return "443"; }
334 return is_demo_ ?
"mockapi.kiwoom.com" :
"api.kiwoom.com";
341 const char*
ws_port() const noexcept {
return "10000"; }
362 static constexpr std::string_view
token_field() noexcept {
return "token"; }
370 if (
const auto expiry = doc.if_contains(
"expires_dt"); expiry && expiry->is_string()) {
373 if (
const auto type = doc.if_contains(
"token_type"); type && type->is_string()) {
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 Kiwoom Securities REST API client.
constexpr const char * tape_suffix(MarketTape tape) noexcept
Returns the symbol suffix for a tape.
MarketTape
Which venue's prices Kiwoom market-data requests read (see KiwoomConfig::market_tape_).
@ CONSOLIDATED
KRX and NXT combined, the SOR book (_AL).
@ KRX
KRX alone (no suffix).
KiwoomSellableScope
What a Kiwoom holdings row's tradable quantity (trde_able_qty) covers; undocumented,...
@ UNSPECIFIED
Not stated: the sellable basis is unestablished, and a sell checked against it is refused.
@ WHOLE_HOLDING
Asserted: it covers every share held of the instrument, on any venue.
std::chrono::system_clock::time_point parse_compact_expiry(std::string_view text)
Reads Kiwoom's token expiry, yyyyMMddHHmmss in Korean time.
constexpr bool kiwoom_position_scope_asserted(KiwoomPositionScope scope) noexcept
Tests whether a holdings scope states what Kiwoom's holdings figure covers, so holdings may be read a...
KiwoomPositionScope
What the venue named on a Kiwoom holdings request (kt00018's dmst_stex_tp) means for the positions it...
@ UNSPECIFIED
Not stated: every new order and amendment is refused (cancellations are not), and the session's holdi...
@ VALUATION_ONLY
Asserted: the venue only selects which venue's price values each position, and every position is list...
The library: broker-neutral types (core), and the modules below.
Venue
A trading venue: an exchange, or the combined view across exchanges.
Kiwoom credentials (app key and secret key), demo or real account, the market-data tape,...
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.
static constexpr std::string_view secret_field() noexcept
The JSON field Kiwoom expects the application secret under.
const char * rest_port() const noexcept
The REST port, which is the same for both environments.
const char * ws_host() const noexcept
The WebSocket host for the selected environment.
KiwoomPositionScope holdings_position_scope_
Your assertion of what holdings_venue_ means for the positions listed. No default: until it is set,...
bool is_demo_
Whether to use Kiwoom's demo (모의투자) endpoints; true unless stated otherwise.
static constexpr std::string_view key_field() noexcept
The JSON field Kiwoom expects the application key under.
MarketTape market_tape_
Which venue's prices every Kiwoom market-data request reads (quotes, depth, trades,...
std::string tape_symbol(std::string_view symbol) const
Returns a symbol with this configuration's tape suffix, for a market-data request.
KiwoomSellableScope holdings_sellable_scope_
Your assertion of what a holdings row's tradable quantity covers; separate from the position scope,...
const char * ws_port() const noexcept
The WebSocket port, which is the same for both environments.
static bool demo_from_json(const json::object &doc, const std::string &filename)
Reads a credential file's "demo".
void from_env_impl()
Reads the credentials from KIWOOM_APP_KEY and KIWOOM_SECRET_KEY, and the environment from KIWOOM_DEMO...
static bool demo_from_env()
Reads KIWOOM_DEMO.
const char * rest_host() const noexcept
The REST host for the selected environment.
Venue holdings_venue_
The venue holdings requests (kt00018) name: Venue::KRX or Venue::NXT. No default: holdings requests a...
A Kiwoom REST access token (au10001's token) and its expiry (expires_dt).
std::string token_type_
Kiwoom's token_type (bearer in its example).
static constexpr std::string_view token_field() noexcept
The field Kiwoom returns the bearer token under.
void from_json_impl(const json::object &doc)
Reads the expiry (expires_dt, the only one Kiwoom states) and token_type; called by from_json....
An OAuth access token and its expiry, the base of each broker's token-response class.
std::chrono::system_clock::time_point expires_at_