feat: introduce mnie
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# client-sbi
|
||||
|
||||
SBI provider client for Mnie.
|
||||
|
||||
The public finance method contract is exported from `@repo/mnie-types`; this package contains SBI-specific login, parsing, and transport code.
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@repo/sbi-client",
|
||||
"name": "@repo/client-sbi",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"module": "index.ts",
|
||||
@@ -15,6 +15,7 @@
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/mnie-types": "workspace:*",
|
||||
"iconv-lite": "^0.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -6,6 +6,7 @@ export {
|
||||
} from './methods/error-map'
|
||||
export type { SbiServerErrorOptions } from './methods/error-map'
|
||||
export { loginWithPasskey } from './session'
|
||||
export type * from '@repo/mnie-types'
|
||||
export type {
|
||||
LoginWithPasskeyOptions,
|
||||
PasskeyLoginResponse,
|
||||
@@ -0,0 +1 @@
|
||||
export type * from '@repo/mnie-types'
|
||||
@@ -207,7 +207,7 @@ const resolveForeignStockEndpointConfig = (
|
||||
restUrl: optionalUrl(restUrl) ?? requiredDerivedUrl(baseUrl, '/rest/'),
|
||||
graphqlBffUrl: optionalUrl(graphqlBffUrl) ?? requiredDerivedUrl(baseUrl, '/graphql/bff'),
|
||||
graphqlIntUrl: optionalUrl(graphqlIntUrl) ?? requiredDerivedUrl(baseUrl, '/graphql/int'),
|
||||
userAgent: userAgent || 'SBIFStockAndroid/1.6.10(csbi/0)',
|
||||
userAgent: userAgent || 'SBIFStockAndroid/1.6.10(mnie/0)',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@repo/mnie-cli",
|
||||
"private": true,
|
||||
"bin": {
|
||||
"mnie": "./src/index.ts"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@napi-rs/keyring": "^1.2.0",
|
||||
"@repo/mnie-sdk": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/env bun
|
||||
import { createHash, randomBytes } from 'node:crypto'
|
||||
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { homedir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { createMnieClient } from '@repo/mnie-sdk'
|
||||
|
||||
type Profile = {
|
||||
origin: string
|
||||
apiKeyStorage: 'file' | 'keyring'
|
||||
apiKey?: string
|
||||
keyringAccount?: string
|
||||
}
|
||||
|
||||
type ProfilesFile = {
|
||||
defaultProfile?: string
|
||||
profiles: Record<string, Profile>
|
||||
}
|
||||
|
||||
const SERVICE = 'mnie-cli'
|
||||
const configDir = join(homedir(), '.mnie-cli')
|
||||
const configPath = join(configDir, 'profiles.json')
|
||||
|
||||
const help = `mnie cli
|
||||
|
||||
Usage:
|
||||
mnie --help
|
||||
mnie profile add <name> --origin <origin> --api-key <key> [--storage file|keyring]
|
||||
mnie profile list
|
||||
mnie profile use <name>
|
||||
mnie rpc methods [--profile <name>]
|
||||
mnie rpc call <method> [json-params] [--profile <name>] [--passkey-id <id>]
|
||||
mnie login --origin <origin> [--profile <name>] [--scopes <scopes>] [--storage file|keyring]
|
||||
|
||||
Examples:
|
||||
mnie profile add local --origin http://127.0.0.1:8787 --api-key mnie_xxx
|
||||
mnie rpc call account.profile --passkey-id sbi_xxx
|
||||
`
|
||||
|
||||
const parseOptions = (args: string[]) => {
|
||||
const positionals: string[] = []
|
||||
const options: Record<string, string | true> = {}
|
||||
for (let index = 0; index < args.length; index++) {
|
||||
const arg = args[index]!
|
||||
if (!arg.startsWith('--')) {
|
||||
positionals.push(arg)
|
||||
continue
|
||||
}
|
||||
const key = arg.slice(2)
|
||||
const next = args[index + 1]
|
||||
if (!next || next.startsWith('--')) {
|
||||
options[key] = true
|
||||
continue
|
||||
}
|
||||
options[key] = next
|
||||
index++
|
||||
}
|
||||
return { positionals, options }
|
||||
}
|
||||
|
||||
const option = (options: Record<string, string | true>, key: string) => {
|
||||
const value = options[key]
|
||||
return typeof value === 'string' ? value : undefined
|
||||
}
|
||||
|
||||
const isString = (value: string | undefined): value is string => typeof value === 'string'
|
||||
|
||||
const loadProfiles = async (): Promise<ProfilesFile> => {
|
||||
try {
|
||||
return JSON.parse(await readFile(configPath, 'utf8')) as ProfilesFile
|
||||
} catch (cause) {
|
||||
if (cause && typeof cause === 'object' && 'code' in cause && cause.code === 'ENOENT') {
|
||||
return { profiles: {} }
|
||||
}
|
||||
throw cause
|
||||
}
|
||||
}
|
||||
|
||||
const saveProfiles = async (profiles: ProfilesFile) => {
|
||||
await mkdir(dirname(configPath), { recursive: true })
|
||||
await writeFile(configPath, `${JSON.stringify(profiles, null, 2)}\n`, { mode: 0o600 })
|
||||
await chmod(configPath, 0o600)
|
||||
}
|
||||
|
||||
const keyring = async () => import('@napi-rs/keyring/keytar')
|
||||
|
||||
const saveApiKey = async (
|
||||
name: string,
|
||||
origin: string,
|
||||
apiKey: string,
|
||||
storage: 'file' | 'keyring',
|
||||
) => {
|
||||
if (storage === 'file') return { apiKey, apiKeyStorage: storage } as const
|
||||
const account = `profile:${name}`
|
||||
const { setPassword } = await keyring()
|
||||
await setPassword(SERVICE, account, JSON.stringify({ origin, apiKey }))
|
||||
return { keyringAccount: account, apiKeyStorage: storage } as const
|
||||
}
|
||||
|
||||
const readApiKey = async (name: string, profile: Profile) => {
|
||||
if (profile.apiKeyStorage === 'file') {
|
||||
if (!profile.apiKey) throw new Error(`profile ${name} has no file api key`)
|
||||
return profile.apiKey
|
||||
}
|
||||
if (!profile.keyringAccount) throw new Error(`profile ${name} has no keyring account`)
|
||||
const { getPassword } = await keyring()
|
||||
const payload = await getPassword(SERVICE, profile.keyringAccount)
|
||||
if (!payload) throw new Error(`profile ${name} api key was not found in keyring`)
|
||||
return (JSON.parse(payload) as { apiKey: string }).apiKey
|
||||
}
|
||||
|
||||
const requireProfile = async (name?: string) => {
|
||||
const profiles = await loadProfiles()
|
||||
const selected = name ?? profiles.defaultProfile
|
||||
if (!selected) throw new Error('profile is required')
|
||||
const profile = profiles.profiles[selected]
|
||||
if (!profile) throw new Error(`profile not found: ${selected}`)
|
||||
return { name: selected, profile, apiKey: await readApiKey(selected, profile) }
|
||||
}
|
||||
|
||||
const printJson = (value: unknown) => {
|
||||
console.log(JSON.stringify(value, null, 2))
|
||||
}
|
||||
|
||||
const addProfile = async (args: string[]) => {
|
||||
const { positionals, options } = parseOptions(args)
|
||||
const name = positionals[0]
|
||||
const origin = option(options, 'origin')
|
||||
const apiKey = option(options, 'api-key')
|
||||
const storage = option(options, 'storage') ?? 'file'
|
||||
if (!name || !origin || !apiKey)
|
||||
throw new Error('profile add requires name, --origin and --api-key')
|
||||
if (storage !== 'file' && storage !== 'keyring')
|
||||
throw new Error('--storage must be file or keyring')
|
||||
const profiles = await loadProfiles()
|
||||
profiles.profiles[name] = {
|
||||
origin: new URL(origin).origin,
|
||||
...(await saveApiKey(name, new URL(origin).origin, apiKey, storage)),
|
||||
}
|
||||
profiles.defaultProfile ??= name
|
||||
await saveProfiles(profiles)
|
||||
printJson({ ok: true, profile: name })
|
||||
}
|
||||
|
||||
const login = async (args: string[]) => {
|
||||
const { options } = parseOptions(args)
|
||||
const origin = option(options, 'origin')
|
||||
if (!origin) throw new Error('login requires --origin')
|
||||
const profileName = option(options, 'profile') ?? 'default'
|
||||
const scopes = option(options, 'scopes') ?? 'mcp read write trade'
|
||||
const storage = option(options, 'storage') ?? 'keyring'
|
||||
if (storage !== 'file' && storage !== 'keyring')
|
||||
throw new Error('--storage must be file or keyring')
|
||||
|
||||
const callback = await listenForOAuthCallback()
|
||||
const redirectUri = `http://127.0.0.1:${callback.port}/callback`
|
||||
const verifier = randomBytes(32).toString('base64url')
|
||||
const challenge = createHash('sha256').update(verifier).digest('base64url')
|
||||
const client = await registerOAuthClient(new URL(origin).origin, redirectUri)
|
||||
const authorize = new URL('/authorize', origin)
|
||||
authorize.searchParams.set('response_type', 'code')
|
||||
authorize.searchParams.set('client_id', client.client_id)
|
||||
authorize.searchParams.set('redirect_uri', redirectUri)
|
||||
authorize.searchParams.set('code_challenge', challenge)
|
||||
authorize.searchParams.set('code_challenge_method', 'S256')
|
||||
authorize.searchParams.set('scope', scopes)
|
||||
|
||||
console.error(`Open this URL to approve Mnie CLI:\n${authorize.toString()}`)
|
||||
openBrowser(authorize.toString())
|
||||
const code = await callback.code
|
||||
const tokens = await exchangeCode(new URL(origin).origin, {
|
||||
clientId: client.client_id,
|
||||
redirectUri,
|
||||
code,
|
||||
verifier,
|
||||
})
|
||||
const profiles = await loadProfiles()
|
||||
profiles.profiles[profileName] = {
|
||||
origin: new URL(origin).origin,
|
||||
...(await saveApiKey(profileName, new URL(origin).origin, tokens.access_token, storage)),
|
||||
}
|
||||
profiles.defaultProfile = profileName
|
||||
await saveProfiles(profiles)
|
||||
printJson({ ok: true, profile: profileName, scope: tokens.scope })
|
||||
}
|
||||
|
||||
const listenForOAuthCallback = async () => {
|
||||
let resolveCode!: (code: string) => void
|
||||
let rejectCode!: (error: Error) => void
|
||||
const code = new Promise<string>((resolve, reject) => {
|
||||
resolveCode = resolve
|
||||
rejectCode = reject
|
||||
})
|
||||
const server = Bun.serve({
|
||||
hostname: '127.0.0.1',
|
||||
port: 0,
|
||||
fetch(request) {
|
||||
const url = new URL(request.url)
|
||||
const error = url.searchParams.get('error')
|
||||
const value = url.searchParams.get('code')
|
||||
queueMicrotask(() => server.stop(true))
|
||||
if (error) {
|
||||
rejectCode(new Error(error))
|
||||
return new Response('Mnie login failed. You can close this tab.', { status: 400 })
|
||||
}
|
||||
if (!value) {
|
||||
rejectCode(new Error('oauth callback did not include a code'))
|
||||
return new Response('Mnie login failed. You can close this tab.', { status: 400 })
|
||||
}
|
||||
resolveCode(value)
|
||||
return new Response('Mnie login complete. You can close this tab.')
|
||||
},
|
||||
})
|
||||
return { port: server.port, code }
|
||||
}
|
||||
|
||||
const registerOAuthClient = async (origin: string, redirectUri: string) => {
|
||||
const response = await fetch(new URL('/register', origin), {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
client_name: 'Mnie CLI',
|
||||
redirect_uris: [redirectUri],
|
||||
grant_types: ['authorization_code', 'refresh_token'],
|
||||
response_types: ['code'],
|
||||
token_endpoint_auth_method: 'none',
|
||||
}),
|
||||
})
|
||||
if (!response.ok) throw new Error(await response.text())
|
||||
return response.json() as Promise<{ client_id: string }>
|
||||
}
|
||||
|
||||
const exchangeCode = async (
|
||||
origin: string,
|
||||
options: { clientId: string; redirectUri: string; code: string; verifier: string },
|
||||
) => {
|
||||
const body = new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
client_id: options.clientId,
|
||||
redirect_uri: options.redirectUri,
|
||||
code: options.code,
|
||||
code_verifier: options.verifier,
|
||||
})
|
||||
const response = await fetch(new URL('/token', origin), {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||
body,
|
||||
})
|
||||
if (!response.ok) throw new Error(await response.text())
|
||||
return response.json() as Promise<{ access_token: string; scope?: string }>
|
||||
}
|
||||
|
||||
const openBrowser = (url: string) => {
|
||||
const command =
|
||||
process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'
|
||||
const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url]
|
||||
Bun.spawn({ cmd: [command, ...args], stdout: 'ignore', stderr: 'ignore' })
|
||||
}
|
||||
|
||||
const rpc = async (args: string[]) => {
|
||||
const { positionals, options } = parseOptions(args)
|
||||
const { name, profile, apiKey } = await requireProfile(option(options, 'profile'))
|
||||
const client = createMnieClient({ origin: profile.origin, apiKey })
|
||||
try {
|
||||
if (positionals[0] === 'methods') {
|
||||
printJson(await client.methods())
|
||||
return
|
||||
}
|
||||
if (positionals[0] !== 'call' || !positionals[1])
|
||||
throw new Error('rpc requires methods or call')
|
||||
const passkeyId = option(options, 'passkey-id')
|
||||
if (passkeyId) await client.connect({ passkeyId })
|
||||
const params = positionals[2] ? JSON.parse(positionals[2]) : undefined
|
||||
const method = positionals[1]
|
||||
const result = await method.split('.').reduce<unknown>((target, key) => {
|
||||
if (!target || (typeof target !== 'object' && typeof target !== 'function')) return undefined
|
||||
return (target as Record<string, unknown>)[key]
|
||||
}, client)
|
||||
if (typeof result !== 'function') throw new Error(`method is not callable: ${method}`)
|
||||
printJson(await result(params))
|
||||
} finally {
|
||||
client.close()
|
||||
console.error(`profile: ${name}`)
|
||||
}
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
const [command, subcommand, ...rest] = Bun.argv.slice(2)
|
||||
if (!command || command === '--help' || command === '-h') {
|
||||
console.log(help)
|
||||
return
|
||||
}
|
||||
if (command === 'profile' && subcommand === 'add') return addProfile(rest)
|
||||
if (command === 'profile' && subcommand === 'list') return printJson(await loadProfiles())
|
||||
if (command === 'profile' && subcommand === 'use') {
|
||||
const [name] = rest
|
||||
if (!name) throw new Error('profile use requires a name')
|
||||
const profiles = await loadProfiles()
|
||||
if (!profiles.profiles[name]) throw new Error(`profile not found: ${name}`)
|
||||
profiles.defaultProfile = name
|
||||
await saveProfiles(profiles)
|
||||
return printJson({ ok: true, profile: name })
|
||||
}
|
||||
if (command === 'rpc') return rpc([subcommand, ...rest].filter(isString))
|
||||
if (command === 'login') return login([subcommand, ...rest].filter(isString))
|
||||
throw new Error(`unknown command: ${command}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : String(error))
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@repo/mnie-sdk",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/mnie-types": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import type { SbiClientMethods } from '@repo/mnie-types'
|
||||
|
||||
export type MnieSdkOptions = {
|
||||
origin: string
|
||||
apiKey: string
|
||||
WebSocket?: typeof WebSocket
|
||||
}
|
||||
|
||||
export type JsonRpcError = {
|
||||
code: number
|
||||
message: string
|
||||
}
|
||||
|
||||
export type MnieClient = SbiClientMethods & {
|
||||
connect(options: { passkeyId: string }): Promise<{ connected: true; passkeyId: string }>
|
||||
methods(): Promise<string[]>
|
||||
close(): void
|
||||
}
|
||||
|
||||
type JsonRpcResponse = {
|
||||
jsonrpc?: '2.0'
|
||||
id?: string | number | null
|
||||
result?: unknown
|
||||
error?: JsonRpcError
|
||||
}
|
||||
|
||||
type PendingCall = {
|
||||
resolve: (value: unknown) => void
|
||||
reject: (error: Error) => void
|
||||
}
|
||||
|
||||
const websocketUrl = (origin: string, apiKey: string) => {
|
||||
const url = new URL('/api/ws', origin)
|
||||
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
url.searchParams.set('key', apiKey)
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
class MnieJsonRpcClient {
|
||||
readonly #socket: WebSocket
|
||||
readonly #pending = new Map<string, PendingCall>()
|
||||
#nextId = 1
|
||||
#opened: Promise<void>
|
||||
|
||||
constructor(options: MnieSdkOptions) {
|
||||
const WebSocketCtor = options.WebSocket ?? WebSocket
|
||||
this.#socket = new WebSocketCtor(websocketUrl(options.origin, options.apiKey))
|
||||
this.#opened = new Promise((resolve, reject) => {
|
||||
this.#socket.addEventListener('open', () => resolve(), { once: true })
|
||||
this.#socket.addEventListener(
|
||||
'error',
|
||||
() => reject(new Error('failed to connect to Mnie RPC')),
|
||||
{ once: true },
|
||||
)
|
||||
})
|
||||
this.#socket.addEventListener('message', (event) => this.#handleMessage(event))
|
||||
this.#socket.addEventListener('close', () => this.#rejectPending('Mnie RPC socket closed'))
|
||||
}
|
||||
|
||||
close() {
|
||||
this.#socket.close()
|
||||
}
|
||||
|
||||
async call(method: string, params?: unknown) {
|
||||
await this.#opened
|
||||
const id = String(this.#nextId++)
|
||||
const response = new Promise((resolve, reject) => {
|
||||
this.#pending.set(id, { resolve, reject })
|
||||
})
|
||||
this.#socket.send(JSON.stringify({ jsonrpc: '2.0', id, method, params }))
|
||||
return response
|
||||
}
|
||||
|
||||
#handleMessage(event: MessageEvent) {
|
||||
const message = JSON.parse(String(event.data)) as JsonRpcResponse
|
||||
if (message.id == null) return
|
||||
const pending = this.#pending.get(String(message.id))
|
||||
if (!pending) return
|
||||
this.#pending.delete(String(message.id))
|
||||
if (message.error) {
|
||||
pending.reject(new Error(message.error.message))
|
||||
return
|
||||
}
|
||||
pending.resolve(message.result)
|
||||
}
|
||||
|
||||
#rejectPending(message: string) {
|
||||
for (const pending of this.#pending.values()) pending.reject(new Error(message))
|
||||
this.#pending.clear()
|
||||
}
|
||||
}
|
||||
|
||||
const methodProxy = (rpc: MnieJsonRpcClient, path: string[] = []): unknown =>
|
||||
new Proxy(() => undefined, {
|
||||
get(_target, property) {
|
||||
if (property === 'then') return undefined
|
||||
if (property === 'connect') {
|
||||
return (params: { passkeyId: string }) => rpc.call('sbi.connect', params)
|
||||
}
|
||||
if (property === 'methods') return () => rpc.call('rpc.methods')
|
||||
if (property === 'close') return () => rpc.close()
|
||||
if (typeof property !== 'string') return undefined
|
||||
return methodProxy(rpc, [...path, property])
|
||||
},
|
||||
apply(_target, _thisArg, args) {
|
||||
return rpc.call(path.join('.'), args.length > 1 ? args : args[0])
|
||||
},
|
||||
})
|
||||
|
||||
export const createMnieClient = (options: MnieSdkOptions) =>
|
||||
methodProxy(new MnieJsonRpcClient(options)) as MnieClient
|
||||
|
||||
export type * from '@repo/mnie-types'
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@repo/mnie-types",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export type * from './types'
|
||||
export type * from './methods'
|
||||
@@ -40,7 +40,7 @@ import type {
|
||||
TradeRecordList,
|
||||
TradeSide,
|
||||
Watchlist,
|
||||
} from '../types'
|
||||
} from './types'
|
||||
|
||||
export type PagingOptions = {
|
||||
/** Start index for the result list. Defaults to the first item when omitted. */
|
||||
@@ -0,0 +1,951 @@
|
||||
export type WebAuthnAlgorithm = -7 | -257
|
||||
|
||||
export type WebAuthnUserVerification = 'required' | 'preferred' | 'discouraged'
|
||||
|
||||
export type WebAuthnTransport = 'ble' | 'hybrid' | 'internal' | 'nfc' | 'usb'
|
||||
|
||||
export type WebAuthnJwk = {
|
||||
kty: string
|
||||
crv?: string
|
||||
x?: string
|
||||
y?: string
|
||||
d?: string
|
||||
n?: string
|
||||
e?: string
|
||||
key_ops?: string[]
|
||||
ext?: boolean
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type StoredWebAuthnCredentialSecret = {
|
||||
privateKey: {
|
||||
format: 'jwk'
|
||||
jwk: WebAuthnJwk
|
||||
}
|
||||
cosePrivateKey?: string
|
||||
registration?: {
|
||||
attestationObject?: string
|
||||
clientDataJSON?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type StoredWebAuthnCredential = {
|
||||
version: 1
|
||||
kind: 'webauthn-credential'
|
||||
provider: 'sbi-sec'
|
||||
rpId: string
|
||||
origin: string
|
||||
credentialId: string
|
||||
userHandle?: string
|
||||
alg: WebAuthnAlgorithm
|
||||
publicKey: {
|
||||
format: 'jwk'
|
||||
jwk: WebAuthnJwk
|
||||
}
|
||||
authenticator: {
|
||||
aaguid?: string
|
||||
signCount: number
|
||||
discoverable: boolean
|
||||
userVerification: WebAuthnUserVerification
|
||||
transports?: WebAuthnTransport[]
|
||||
backupEligible?: boolean
|
||||
backupState?: boolean
|
||||
}
|
||||
secret: {
|
||||
encrypted: true
|
||||
format: 'jwe-like-v1'
|
||||
kdf: {
|
||||
name: 'argon2id' | 'scrypt'
|
||||
salt: string
|
||||
params: Record<string, unknown>
|
||||
}
|
||||
cipher: {
|
||||
name: 'AES-256-GCM'
|
||||
nonce: string
|
||||
aad: string
|
||||
ciphertext: string
|
||||
tag: string
|
||||
}
|
||||
}
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type PlaintextStoredWebAuthnCredential = Omit<StoredWebAuthnCredential, 'secret'> & {
|
||||
label?: string
|
||||
secretPlaintext: StoredWebAuthnCredentialSecret
|
||||
}
|
||||
|
||||
export type PasskeyLoginResponse = {
|
||||
type: 'passkey-login-response'
|
||||
requestUrl: string
|
||||
status: number
|
||||
body: ArrayBuffer
|
||||
text: string
|
||||
accessToken?: string
|
||||
header: {
|
||||
sessionId: string
|
||||
trCode: string
|
||||
resultCode: string
|
||||
} | null
|
||||
}
|
||||
|
||||
export type ForeignStockEndpointConfig = {
|
||||
baseUrl?: string
|
||||
restUrl: string
|
||||
graphqlBffUrl: string
|
||||
graphqlIntUrl: string
|
||||
userAgent?: string
|
||||
}
|
||||
|
||||
export type ForeignStockSession = {
|
||||
endpoints: ForeignStockEndpointConfig
|
||||
ssoToken?: string
|
||||
sessionId?: string
|
||||
accountId?: string
|
||||
marketPriceHash?: string
|
||||
candleHash?: string
|
||||
loginAuthenticated?: boolean
|
||||
}
|
||||
|
||||
export type MainSiteAuthCache = {
|
||||
baseUrl: string
|
||||
assetsUrl: string
|
||||
cookieHeader: string
|
||||
authenticatedAt: string
|
||||
}
|
||||
|
||||
export type MainSiteSession = {
|
||||
baseUrl?: string
|
||||
etGatePath?: string
|
||||
assetsValuationsPath?: string
|
||||
exchangeOrderInputPath?: string
|
||||
exchangeOrderPasswordPath?: string
|
||||
exchangeOrderConfirmPath?: string
|
||||
exchangeOrderCompletePath?: string
|
||||
auth?: MainSiteAuthCache
|
||||
authPromise?: Promise<MainSiteAuthCache>
|
||||
}
|
||||
|
||||
export type SbiSession = {
|
||||
mtsBaseUrl: string
|
||||
izanagiBaseUrl?: string
|
||||
foreignStock?: ForeignStockSession
|
||||
mainSite?: MainSiteSession
|
||||
profile: AccountProfile
|
||||
loginResponse: PasskeyLoginResponse
|
||||
tradePassword?: string
|
||||
deviceIdRegistered?: boolean
|
||||
tradeAuthentication?: SbiTradeAuthenticationOptions
|
||||
}
|
||||
|
||||
export type LoginWithPasskeyOptions = {
|
||||
passkeyCredential: PlaintextStoredWebAuthnCredential
|
||||
authBaseUrl?: string
|
||||
mtsBaseUrl?: string
|
||||
izanagiBaseUrl?: string
|
||||
foreignStockBaseUrl?: string
|
||||
usStockBaseUrl?: string
|
||||
foreignStockRestUrl?: string
|
||||
foreignStockGraphqlBffUrl?: string
|
||||
foreignStockGraphqlIntUrl?: string
|
||||
foreignStockUserAgent?: string
|
||||
mainSiteBaseUrl?: string
|
||||
mainSiteEtGatePath?: string
|
||||
mainSiteAssetsValuationsPath?: string
|
||||
mainSiteExchangeOrderInputPath?: string
|
||||
mainSiteExchangeOrderPasswordPath?: string
|
||||
mainSiteExchangeOrderConfirmPath?: string
|
||||
mainSiteExchangeOrderCompletePath?: string
|
||||
}
|
||||
|
||||
export type SbiClientOptions = {
|
||||
tradePassword?: string
|
||||
deviceId?: string
|
||||
tradeAuthentication?: SbiTradeAuthenticationOptions
|
||||
}
|
||||
|
||||
export type SbiTradeAuthenticationRequest = {
|
||||
type: 'phone'
|
||||
telNo?: string
|
||||
phoneNo?: string
|
||||
sbiCallNo?: string
|
||||
authLimitTime?: string
|
||||
}
|
||||
|
||||
export type SbiTradeAuthenticationOptions = {
|
||||
onRequired?: (request: SbiTradeAuthenticationRequest) => void | Promise<void>
|
||||
confirmAttempts?: number
|
||||
confirmIntervalMs?: number
|
||||
}
|
||||
|
||||
export type IssueCode = string
|
||||
export type DomesticMarketCode = 'XTKS' | 'XNGO' | 'XFKA' | 'XSAP'
|
||||
export type SKabuMarketCode = 'STK'
|
||||
export type UsStockMarketCode = 'XNAS' | 'XNYS' | 'ARCX'
|
||||
export type MarketCode = DomesticMarketCode | SKabuMarketCode | UsStockMarketCode
|
||||
export type OrderId = string
|
||||
export type WatchlistId = string
|
||||
export type PositionId = string
|
||||
export type ThemeId = string
|
||||
|
||||
export type CurrencyAmount = {
|
||||
value: number | null
|
||||
text: string
|
||||
currency: 'JPY' | 'USD'
|
||||
}
|
||||
|
||||
export type PercentValue = {
|
||||
value: number | null
|
||||
text: string
|
||||
}
|
||||
|
||||
export type SignedTextValue = {
|
||||
value: number | null
|
||||
text: string
|
||||
sign?: 'positive' | 'negative' | 'zero'
|
||||
}
|
||||
|
||||
export type AccountAssetsValuationSummary = {
|
||||
assetsErrorType: unknown
|
||||
valuation: number | null
|
||||
netChange: number | null
|
||||
percentChange: number | null
|
||||
monthOnMonth: number | null
|
||||
monthOnMonthRatio: unknown
|
||||
profitLoss: number | null
|
||||
profitLossRate: number | null
|
||||
acquisitionCost: number | null
|
||||
}
|
||||
|
||||
export type AccountAssetsValuationDetail = AccountAssetsValuationSummary & {
|
||||
category: string
|
||||
compositionRatio: number | null
|
||||
}
|
||||
|
||||
export type AccountAssetsValuations = {
|
||||
fetchedAt: string
|
||||
summary: AccountAssetsValuationSummary
|
||||
summaryWithoutDeposit: AccountAssetsValuationSummary
|
||||
summaryWithoutIdeco?: AccountAssetsValuationSummary
|
||||
summaryWithoutDepositAndIdeco?: AccountAssetsValuationSummary
|
||||
summaryDetails: AccountAssetsValuationDetail[]
|
||||
summaryDetailsWithoutDeposit: AccountAssetsValuationDetail[]
|
||||
summaryDetailsWithoutIdeco: AccountAssetsValuationDetail[]
|
||||
summaryDetailsWithoutDepositAndIdeco: AccountAssetsValuationDetail[]
|
||||
}
|
||||
|
||||
export type AccountType =
|
||||
| 'general'
|
||||
| 'specific'
|
||||
| 'growthInvestment'
|
||||
| 'nisa'
|
||||
| 'juniorNisa'
|
||||
| 'unknown'
|
||||
export type DepositType =
|
||||
| 'general'
|
||||
| 'specific'
|
||||
| 'growthInvestment'
|
||||
| 'nisa'
|
||||
| 'juniorNisa'
|
||||
| 'unknown'
|
||||
export type TradeSide = 'buy' | 'sell'
|
||||
export type MarginTradeSide = 'buy' | 'sell'
|
||||
export type OrderStatus = 'open' | 'executed' | 'cancelled' | 'expired' | 'rejected' | 'unknown'
|
||||
export type OrderKind = 'market' | 'limit' | 'stop' | 'oco' | 'ifd' | 'ifdo' | 's' | 'unknown'
|
||||
export type LoginStatus =
|
||||
| 'success'
|
||||
| 'invalidUser'
|
||||
| 'tradeForbidden'
|
||||
| 'locked'
|
||||
| 'fidoAuthorizationIncorrect'
|
||||
| 'fidoAuthorization'
|
||||
| 'passwordChangeRequired'
|
||||
| 'unknown'
|
||||
export type LoginType = 'passkey' | 'password' | 'unknown'
|
||||
export type SpecificAccountType =
|
||||
| 'withHolding'
|
||||
| 'withoutHolding'
|
||||
| 'nonSpecific'
|
||||
| 'notApply'
|
||||
| 'unknown'
|
||||
export type IsaAccountType =
|
||||
| 'nisaTradeForbidden'
|
||||
| 'oldNisaTradePermitted'
|
||||
| 'newNisaTradePermitted'
|
||||
| 'nisaTradePermitted'
|
||||
| 'unknown'
|
||||
|
||||
export type IssueRef = {
|
||||
code: IssueCode
|
||||
market?: MarketCode
|
||||
name?: string
|
||||
}
|
||||
|
||||
export type IssueSearchItem = IssueRef & {
|
||||
extract?: string
|
||||
extractWord?: string
|
||||
boldFrom?: string
|
||||
boldTo?: string
|
||||
hitString?: string
|
||||
}
|
||||
|
||||
export type IssueSearchStatus = 'success' | 'searchError' | 'tooManyResults' | 'unknown'
|
||||
|
||||
export type IssueSearchResult = {
|
||||
status?: string
|
||||
statusText: IssueSearchStatus
|
||||
issues: IssueSearchItem[]
|
||||
}
|
||||
|
||||
export type ChartPeriod = 'minute' | 'day' | 'week' | 'month'
|
||||
|
||||
export type ChartPrice = {
|
||||
dateTime: string
|
||||
open: CurrencyAmount
|
||||
high: CurrencyAmount
|
||||
low: CurrencyAmount
|
||||
close: CurrencyAmount
|
||||
volume?: number | null
|
||||
}
|
||||
|
||||
export type IssueChart = {
|
||||
issue: IssueRef
|
||||
period: ChartPeriod
|
||||
unit: number
|
||||
prices: ChartPrice[]
|
||||
previousClose?: CurrencyAmount
|
||||
currentPrice?: CurrencyAmount
|
||||
highPrice?: CurrencyAmount
|
||||
lowPrice?: CurrencyAmount
|
||||
latestDateTime?: string
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type SessionInfo = {
|
||||
sessionId: string
|
||||
loginType: LoginType
|
||||
resultCode: string
|
||||
}
|
||||
|
||||
export type AccountProfile = {
|
||||
session: SessionInfo
|
||||
branchCode?: string
|
||||
butenCode?: string
|
||||
accountNumber?: string
|
||||
userId?: string
|
||||
loginStatus?: LoginStatus
|
||||
loginType?: LoginType
|
||||
accountType?: AccountType
|
||||
specificAccountType?: SpecificAccountType
|
||||
hasMarginAccount?: boolean
|
||||
marginAccount?: string
|
||||
corporateFlag?: string
|
||||
commissionPlan?: string
|
||||
expireDate?: string
|
||||
lastLoginDate?: string
|
||||
lastLoginTime?: string
|
||||
tradingPassword?: string
|
||||
fxShareCol?: string
|
||||
fullTerm?: string
|
||||
fullAccount?: string
|
||||
securityAuthenticationResponseCode?: string
|
||||
fidoResponseCode?: string
|
||||
passkeyStatus?: string
|
||||
trId?: string
|
||||
txId?: string
|
||||
actionToken?: string
|
||||
nisa?: {
|
||||
enabled: boolean
|
||||
tradePermitted?: boolean
|
||||
juniorEnabled?: boolean
|
||||
accountType?: IsaAccountType
|
||||
jrNisaAccount?: string
|
||||
jrNisaSpecific?: SpecificAccountType
|
||||
jrNisaSeigen?: string
|
||||
}
|
||||
sor?: {
|
||||
defaultEnabled?: boolean
|
||||
defaultCode?: string
|
||||
lastMarket?: MarketCode
|
||||
juniorNisaLastMarket?: MarketCode
|
||||
}
|
||||
notices?: {
|
||||
hasImportantNotice?: boolean
|
||||
importantNoticeFlag?: string
|
||||
count?: number
|
||||
}
|
||||
restrictions?: {
|
||||
tradeRestricted?: boolean
|
||||
restrictedTradeFlag?: string
|
||||
message?: string
|
||||
}
|
||||
deficit?: {
|
||||
hasMessage?: boolean
|
||||
messageFlag?: string
|
||||
message?: string
|
||||
}
|
||||
maintenance?: {
|
||||
referenceable?: boolean
|
||||
referenceableMaintenanceFlag?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type BuyingPower = {
|
||||
cashBuyingPower?: CurrencyAmount
|
||||
marginBuyingPower?: CurrencyAmount
|
||||
withdrawableAmount?: CurrencyAmount
|
||||
collateralValue?: CurrencyAmount
|
||||
collateralRatio?: PercentValue
|
||||
sbiHybridDepositBalance?: CurrencyAmount
|
||||
noticeMessage?: string
|
||||
records?: CollateralRatioRecord[]
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type CollateralRatioRecord = {
|
||||
marginRequirements?: CurrencyAmount
|
||||
referenceMarginRequirements?: CurrencyAmount
|
||||
collateralRatioCash?: CurrencyAmount
|
||||
substituteSecuritiesValuationAmount?: CurrencyAmount
|
||||
unsettledPositionLoss?: SignedTextValue
|
||||
unsettledPositionLossFlag?: string
|
||||
settlementLoss?: SignedTextValue
|
||||
settlementLossFlag?: string
|
||||
paymentExpenses?: SignedTextValue
|
||||
paymentExpensesFlag?: string
|
||||
actualCollateral?: CurrencyAmount
|
||||
positionAmount?: CurrencyAmount
|
||||
sbiHybridDepositBalance?: CurrencyAmount
|
||||
minimumCollateral?: CurrencyAmount
|
||||
}
|
||||
|
||||
export type CashPosition = {
|
||||
issue: IssueRef
|
||||
accountType?: AccountType
|
||||
depositType?: DepositType
|
||||
depositTypeCode?: string
|
||||
depositTypeText?: string
|
||||
quantity: number | null
|
||||
availableQuantity?: number | null
|
||||
unexecutedOrderQuantity?: number | null
|
||||
averagePrice?: CurrencyAmount
|
||||
purchasePrice?: CurrencyAmount
|
||||
/** Current unit price, not multiplied by quantity. */
|
||||
currentPrice?: CurrencyAmount
|
||||
priceText?: string
|
||||
/** Total position valuation amount. For cash positions this is quantity-adjusted. */
|
||||
marketValue?: CurrencyAmount
|
||||
presentValueFlag?: string
|
||||
/** Raw SBI valuation amount. Kept for source compatibility; prefer `marketValue` for totals. */
|
||||
valuationPrice?: CurrencyAmount
|
||||
valuationPriceChange?: SignedTextValue
|
||||
valuationPriceChangeRate?: PercentValue
|
||||
valuationPriceChangeFlag?: string
|
||||
profitLoss?: SignedTextValue
|
||||
profitLossRate?: PercentValue
|
||||
profitLossFlag?: string
|
||||
holdingCategory?: string
|
||||
accountInformation?: string
|
||||
}
|
||||
|
||||
export type CashPositionList = {
|
||||
positions: CashPosition[]
|
||||
index?: number
|
||||
totalCount?: number
|
||||
totalMarketValue?: CurrencyAmount
|
||||
totalProfitLoss?: SignedTextValue
|
||||
totalProfitLossRate?: PercentValue
|
||||
totalProfitLossFlag?: string
|
||||
hasMore?: boolean
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type MarginPosition = {
|
||||
id?: PositionId
|
||||
issue: IssueRef
|
||||
side: MarginTradeSide
|
||||
sideText?: string
|
||||
accountType?: AccountType
|
||||
tradeKind?: string
|
||||
quantity: number | null
|
||||
availableCloseQuantity?: number | null
|
||||
unexecutedOrderQuantity?: number | null
|
||||
openPrice?: CurrencyAmount
|
||||
openAmount?: CurrencyAmount
|
||||
/** Current unit price or rate, not multiplied by quantity. */
|
||||
currentPrice?: CurrencyAmount
|
||||
rate?: CurrencyAmount
|
||||
/** Total position valuation amount when provided by SBI. */
|
||||
marketValue?: CurrencyAmount
|
||||
presentValueFlag?: string
|
||||
/** Raw SBI valuation amount. Prefer `marketValue` when calculating totals. */
|
||||
valuationPrice?: CurrencyAmount
|
||||
valuationPriceChange?: SignedTextValue
|
||||
valuationPriceChangeRate?: PercentValue
|
||||
valuationPriceChangeFlag?: string
|
||||
profitLoss?: SignedTextValue
|
||||
profitLossRate?: PercentValue
|
||||
profitLossFlag?: string
|
||||
openDate?: string
|
||||
dueDate?: string
|
||||
dueDateCode?: string
|
||||
dueDateText?: string
|
||||
depositTypeText?: string
|
||||
cost?: CurrencyAmount
|
||||
commission?: CurrencyAmount
|
||||
managementFee?: CurrencyAmount
|
||||
nameTransferFee?: CurrencyAmount
|
||||
interest?: CurrencyAmount
|
||||
backwardation?: CurrencyAmount
|
||||
collateralRatio?: PercentValue
|
||||
bargainMarketCode?: string
|
||||
bargainMarket?: string
|
||||
}
|
||||
|
||||
export type MarginPositionList = {
|
||||
positions: MarginPosition[]
|
||||
index?: number
|
||||
totalCount?: number
|
||||
totalMarketValue?: CurrencyAmount
|
||||
totalProfitLoss?: SignedTextValue
|
||||
totalProfitLossRate?: PercentValue
|
||||
totalProfitLossFlag?: string
|
||||
hasMore?: boolean
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type ProfitLossSummary = {
|
||||
cash?: SignedTextValue
|
||||
margin?: SignedTextValue
|
||||
total?: SignedTextValue
|
||||
totalRate?: PercentValue
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type Quote = {
|
||||
issue: IssueRef
|
||||
price?: CurrencyAmount
|
||||
change?: SignedTextValue
|
||||
changeRate?: PercentValue
|
||||
changeFlag?: string
|
||||
open?: CurrencyAmount
|
||||
high?: CurrencyAmount
|
||||
low?: CurrencyAmount
|
||||
previousClose?: CurrencyAmount
|
||||
volume?: number | null
|
||||
timestamp?: string
|
||||
nominalPrices?: CurrencyAmount[]
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type BoardPriceLevel = {
|
||||
price: CurrencyAmount
|
||||
quantity?: number | null
|
||||
}
|
||||
|
||||
export type Board = {
|
||||
issue: IssueRef
|
||||
bids: BoardPriceLevel[]
|
||||
asks: BoardPriceLevel[]
|
||||
quote?: Quote
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type MarketIndex = {
|
||||
code?: string
|
||||
categoryCode?: string
|
||||
name: string
|
||||
value?: number | null
|
||||
valueText?: string
|
||||
change?: SignedTextValue
|
||||
changeRate?: PercentValue
|
||||
colorFlag?: string
|
||||
timestamp?: string
|
||||
open?: CurrencyAmount
|
||||
high?: CurrencyAmount
|
||||
low?: CurrencyAmount
|
||||
previousClose?: CurrencyAmount
|
||||
}
|
||||
|
||||
export type DomesticMarket = {
|
||||
status?: string
|
||||
indexes: MarketIndex[]
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type RankingItem = {
|
||||
rank: number
|
||||
issue: IssueRef
|
||||
value?: number | string | null
|
||||
values?: Array<number | string | null>
|
||||
change?: SignedTextValue
|
||||
changeRate?: PercentValue
|
||||
exchangeName?: string
|
||||
colorFlag?: string
|
||||
}
|
||||
|
||||
export type Ranking = {
|
||||
items: RankingItem[]
|
||||
category?: string
|
||||
updatedAt?: string
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type NewsItem = {
|
||||
id?: string
|
||||
title: string
|
||||
source?: string
|
||||
publishedAt?: string
|
||||
url?: string
|
||||
summary?: string
|
||||
storyDate?: string
|
||||
storyTime?: string
|
||||
processedDate?: string
|
||||
takeTime?: string
|
||||
pnac?: string
|
||||
}
|
||||
|
||||
export type NewsList = {
|
||||
items: NewsItem[]
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type WatchlistItem = {
|
||||
issue: IssueRef
|
||||
sortOrder?: number
|
||||
memo?: string
|
||||
quote?: Quote
|
||||
}
|
||||
|
||||
export type Watchlist = {
|
||||
id: WatchlistId
|
||||
name: string
|
||||
items: WatchlistItem[]
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type Order = {
|
||||
id: OrderId
|
||||
issue: IssueRef
|
||||
side: TradeSide
|
||||
sideText?: string
|
||||
status: OrderStatus
|
||||
statusText?: string
|
||||
executionStatus?: string
|
||||
executionStatusText?: string
|
||||
kind?: OrderKind
|
||||
accountType?: AccountType
|
||||
depositType?: DepositType
|
||||
depositTypeCode?: string
|
||||
depositTypeText?: string
|
||||
quantity?: number | null
|
||||
unexecutedQuantity?: number | null
|
||||
executedQuantity?: number | null
|
||||
price?: CurrencyAmount
|
||||
executedPrice?: CurrencyAmount
|
||||
orderedAt?: string
|
||||
expiresAt?: string
|
||||
orderNumber?: string
|
||||
orderSubNo?: string
|
||||
tradeId?: string
|
||||
exchangeCode?: string
|
||||
accountInformation?: string
|
||||
cancelable?: boolean
|
||||
correctable?: boolean
|
||||
}
|
||||
|
||||
export type OrderList = {
|
||||
orders: Order[]
|
||||
hasMore?: boolean
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type TradeRecord = {
|
||||
id: string
|
||||
issue: IssueRef
|
||||
tradeRecordTypeCode?: string
|
||||
tradeCurrencyCode?: string
|
||||
listedSecuritiesStatus?: string
|
||||
orderPriceKindCode?: string
|
||||
accountType?: AccountType
|
||||
settlementCurrencyCode?: string
|
||||
amount?: CurrencyAmount
|
||||
quantity?: number | null
|
||||
price?: CurrencyAmount
|
||||
tradeDate?: string
|
||||
valueDate?: string
|
||||
marginCloseLimitType?: string
|
||||
}
|
||||
|
||||
export type TradeRecordList = {
|
||||
records: TradeRecord[]
|
||||
hasMore?: boolean
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type OrderCorrectionPreOrderDetail = {
|
||||
exchangeName?: string
|
||||
marketLoanKbn?: string
|
||||
marketIppanLoanKbn?: string
|
||||
currentPrice?: CurrencyAmount
|
||||
tradeColorFlag?: string
|
||||
priceTick?: string
|
||||
priceTickText?: string
|
||||
tradeTime?: string
|
||||
changeText?: string
|
||||
volumeText?: string
|
||||
}
|
||||
|
||||
export type OrderCorrectionPreOrder = {
|
||||
issue: IssueRef
|
||||
tradeTitle?: string
|
||||
buyingPowerTotal?: CurrencyAmount
|
||||
controlledStockCode?: string
|
||||
hasTradeWarning?: boolean
|
||||
deficitMessageFlag?: string
|
||||
deficitMessage?: string
|
||||
details: OrderCorrectionPreOrderDetail[]
|
||||
orderNumber?: string
|
||||
orderId?: string
|
||||
primaryOrderMethod?: string
|
||||
primaryTriggerZone?: string
|
||||
primaryTriggerPrice?: number | null
|
||||
status?: string
|
||||
statusText?: string
|
||||
tradeId?: string
|
||||
tradeName?: string
|
||||
quantity?: number | null
|
||||
quantityText?: string
|
||||
orderLimit?: string
|
||||
orderLimitText?: string
|
||||
priceSteps: StockOrderPreOrderPriceStep[]
|
||||
sessionRange?: string
|
||||
inputDateText?: string
|
||||
primaryOrderTerm?: string
|
||||
nonSpecificTradeText?: string
|
||||
marketName?: string
|
||||
rbeOrderStatus?: string
|
||||
priceCondition?: string
|
||||
price?: number | null
|
||||
priceAmount?: CurrencyAmount
|
||||
exchangeName?: string
|
||||
transId?: string
|
||||
ptsDayNightFlag?: string
|
||||
smallTickFlag?: string
|
||||
juniorBuyingPowerTotal?: CurrencyAmount
|
||||
secondaryPriceCondition?: string
|
||||
secondaryPrice?: number | null
|
||||
secondaryPriceAmount?: CurrencyAmount
|
||||
autoOrderKind?: string
|
||||
autoOrderNumber?: string
|
||||
autoOrderInputDate?: string
|
||||
secondaryOrderMethod?: string
|
||||
secondaryTriggerZone?: string
|
||||
secondaryTriggerPrice?: number | null
|
||||
secondaryOrderCondition?: string
|
||||
secondaryLimitPrice?: number | null
|
||||
secondaryLimitPriceAmount?: CurrencyAmount
|
||||
secondaryOrderTerm?: string
|
||||
secondaryOcoPriceCondition?: string
|
||||
secondaryOcoPrice?: number | null
|
||||
secondaryOcoPriceAmount?: CurrencyAmount
|
||||
exchangeList?: string
|
||||
}
|
||||
|
||||
export type OrderPreview = {
|
||||
issue: IssueRef
|
||||
side: TradeSide
|
||||
quantity?: number
|
||||
price?: CurrencyAmount
|
||||
estimatedAmount?: CurrencyAmount
|
||||
commission?: CurrencyAmount
|
||||
tax?: CurrencyAmount
|
||||
warnings: string[]
|
||||
confirmationId?: string
|
||||
message?: string
|
||||
correction?: OrderCorrectionPreOrder
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type StockOrderPreOrderPriceStep = {
|
||||
from?: CurrencyAmount
|
||||
to?: CurrencyAmount
|
||||
}
|
||||
|
||||
export type StockOrderPreOrderPaymentLimit = {
|
||||
text?: string
|
||||
code?: string
|
||||
}
|
||||
|
||||
export type StockOrderPreOrderMarginTradeType =
|
||||
| 'standard'
|
||||
| 'generalBuy'
|
||||
| 'generalSellShort'
|
||||
| 'generalSellInventoryLimited'
|
||||
| 'generalSellInventoryUnlimited'
|
||||
| 'day'
|
||||
| 'hyper'
|
||||
|
||||
export type StockOrderPreOrder = {
|
||||
issue: IssueRef
|
||||
tradeTitle?: string
|
||||
buyingPowerTotal?: CurrencyAmount
|
||||
controlledStockCode?: string
|
||||
hasTradeWarning?: boolean
|
||||
market?: MarketCode
|
||||
exchangeList?: string
|
||||
exchangeListName?: string
|
||||
exchangeListIndexFlag?: string
|
||||
marketLoanKbn?: string
|
||||
marketIppanLoanKbn?: string
|
||||
currentPrice?: CurrencyAmount
|
||||
tradeColorFlag?: string
|
||||
priceTick?: string
|
||||
priceTickText?: string
|
||||
tradeTime?: string
|
||||
changeText?: string
|
||||
volume?: number | null
|
||||
lotSize?: number | null
|
||||
priceSteps: StockOrderPreOrderPriceStep[]
|
||||
sessionRange?: string
|
||||
basePrice?: CurrencyAmount
|
||||
orderTerms: string[]
|
||||
orderTermDates: string[]
|
||||
paymentLimits: StockOrderPreOrderPaymentLimit[]
|
||||
nonSpecificTradeText?: string
|
||||
paymentLimitText?: string
|
||||
acquisitionPrice?: CurrencyAmount
|
||||
position?: number | null
|
||||
unexecutedQuantity?: number | null
|
||||
lotSize2?: number | null
|
||||
ptsDayNightFlag?: string
|
||||
sorServiceType?: string
|
||||
nisa?: {
|
||||
serviceKbn?: string
|
||||
buyLimit?: CurrencyAmount
|
||||
growthServiceKbn?: string
|
||||
juniorServiceKbn?: string
|
||||
juniorBuyLimit?: CurrencyAmount
|
||||
juniorBuyingPowerTotal?: CurrencyAmount
|
||||
}
|
||||
smallTickFlag?: string
|
||||
margin?: {
|
||||
tradeTypes?: StockOrderPreOrderMarginTradeType[]
|
||||
ippanShort?: string
|
||||
ippanLong?: string
|
||||
dayBuy?: string
|
||||
daySell?: string
|
||||
premiumShortSelling?: string
|
||||
premiumFee?: CurrencyAmount
|
||||
ippanPaymentLimit?: string
|
||||
positionStatus?: string
|
||||
}
|
||||
sKabu?: {
|
||||
code?: string
|
||||
available?: boolean
|
||||
}
|
||||
deficitMessageFlag?: string
|
||||
deficitMessage?: string
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type OrderReceipt = {
|
||||
accepted: boolean
|
||||
orderId?: OrderId
|
||||
acceptedAt?: string
|
||||
message?: string
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type ExchangeOrderSide = 'buy' | 'sell'
|
||||
export type ExchangeSpecificMethod = 'foreign' | 'domestic'
|
||||
export type ExchangeAccountKind = 'GENERAL' | 'JR_NISA'
|
||||
export type ExchangeSellMethod = 'SELL_PART' | 'SELL_ALL'
|
||||
|
||||
export type ExchangeOrderPreview = {
|
||||
currencyCode: string
|
||||
currencyName?: string
|
||||
side: ExchangeOrderSide
|
||||
exchangeType?: string
|
||||
accountKind?: ExchangeAccountKind
|
||||
specificMethod?: ExchangeSpecificMethod
|
||||
sellMethod?: ExchangeSellMethod | null
|
||||
tradeQuantity?: string
|
||||
orderAmount?: string
|
||||
exchangeRate?: string
|
||||
netAmount?: string
|
||||
valueDate?: string
|
||||
rateDateTime?: string
|
||||
warningMessage?: string | null
|
||||
isMaintenance?: boolean
|
||||
csrfToken: string
|
||||
}
|
||||
|
||||
export type ExchangeOrderReceipt = {
|
||||
accepted: boolean
|
||||
currencyCode?: string
|
||||
side?: ExchangeOrderSide
|
||||
message?: string
|
||||
warningMessage?: string | null
|
||||
rawTitle?: string
|
||||
}
|
||||
|
||||
export type ExchangeRateInfo = {
|
||||
currencyCode: string
|
||||
side: ExchangeOrderSide
|
||||
referenceExchangeRate?: string
|
||||
computeExchangeRate?: string
|
||||
basePrice?: string
|
||||
exchangeTradeType?: string
|
||||
updateTime?: string
|
||||
buyPossibleAmount?: string
|
||||
sellPossibleAmount?: string
|
||||
buyUnit?: string
|
||||
sellUnit?: string
|
||||
buyLimitMin?: string
|
||||
buyLimitMax?: string
|
||||
sellLimitMin?: string
|
||||
sellLimitMax?: string
|
||||
raw: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type ThemeInvestment = {
|
||||
id: ThemeId
|
||||
name: string
|
||||
issues: ThemeInvestmentIssue[]
|
||||
minimumAmount?: CurrencyAmount
|
||||
}
|
||||
|
||||
export type ThemeInvestmentIssue = IssueRef & {
|
||||
controlledStockCode?: string
|
||||
hasTradeWarning?: boolean
|
||||
nisaServiceKbn?: string
|
||||
juniorNisaServiceKbn?: string
|
||||
growthNisaServiceKbn?: string
|
||||
sKabuCode?: string
|
||||
sKabuAvailable?: boolean
|
||||
lotSize?: number | null
|
||||
currentPrice?: CurrencyAmount
|
||||
tradeColorFlag?: string
|
||||
priceTick?: string
|
||||
priceTickText?: string
|
||||
tradeTime?: string
|
||||
}
|
||||
|
||||
export type ThemeInvestmentList = {
|
||||
themes: ThemeInvestment[]
|
||||
buyingPowerTotal?: CurrencyAmount
|
||||
isaBuyLimit?: CurrencyAmount
|
||||
juniorNisaBuyLimit?: CurrencyAmount
|
||||
buyingPowerTotalJuniorNisa?: CurrencyAmount
|
||||
deficitMessage?: string
|
||||
deficitMessageFlag?: string
|
||||
error?: SbiMethodError
|
||||
}
|
||||
|
||||
export type SbiMethodError = {
|
||||
status?: string
|
||||
code?: string
|
||||
message?: string
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# sbi-client
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.3.3. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
Reference in New Issue
Block a user