initial commit
This commit is contained in:
14
한국투자증권(API)/legacy/websocket/delphi/KISWebSocket.dpr
Normal file
14
한국투자증권(API)/legacy/websocket/delphi/KISWebSocket.dpr
Normal file
@@ -0,0 +1,14 @@
|
||||
program KISWebSocket;
|
||||
|
||||
uses
|
||||
Vcl.Forms,
|
||||
MainForm in 'MainForm.pas' {Form1};
|
||||
|
||||
{$R *.res}
|
||||
|
||||
begin
|
||||
Application.Initialize;
|
||||
Application.MainFormOnTaskbar := True;
|
||||
Application.CreateForm(TForm1, Form1);
|
||||
Application.Run;
|
||||
end.
|
||||
1119
한국투자증권(API)/legacy/websocket/delphi/KISWebSocket.dproj
Normal file
1119
한국투자증권(API)/legacy/websocket/delphi/KISWebSocket.dproj
Normal file
File diff suppressed because it is too large
Load Diff
183
한국투자증권(API)/legacy/websocket/delphi/MainForm.dfm
Normal file
183
한국투자증권(API)/legacy/websocket/delphi/MainForm.dfm
Normal file
@@ -0,0 +1,183 @@
|
||||
object Form1: TForm1
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = #54620#44397#53804#51088#51613#44428' WebSocket '#53580#49828#53944' v1.0'
|
||||
ClientHeight = 600
|
||||
ClientWidth = 750
|
||||
Color = clBtnFace
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -12
|
||||
Font.Name = 'Segoe UI'
|
||||
Font.Style = []
|
||||
Position = poScreenCenter
|
||||
OnCreate = FormCreate
|
||||
TextHeight = 15
|
||||
object Label1: TLabel
|
||||
Left = 24
|
||||
Top = 24
|
||||
Width = 73
|
||||
Height = 15
|
||||
Caption = 'Approval Key:'
|
||||
end
|
||||
object Label2: TLabel
|
||||
Left = 24
|
||||
Top = 58
|
||||
Width = 32
|
||||
Height = 15
|
||||
Caption = 'TR_ID:'
|
||||
end
|
||||
object Label3: TLabel
|
||||
Left = 24
|
||||
Top = 92
|
||||
Width = 89
|
||||
Height = 15
|
||||
Caption = 'TR_KEY:'#51333#47785#53076#46300
|
||||
end
|
||||
object edtApprovalKey: TEdit
|
||||
Left = 120
|
||||
Top = 21
|
||||
Width = 420
|
||||
Height = 23
|
||||
TabOrder = 0
|
||||
TextHint = 'Approval Key '#51077#47141' (e.g. e44ffe64-7a22-...)'
|
||||
end
|
||||
object edtTrId: TEdit
|
||||
Left = 120
|
||||
Top = 55
|
||||
Width = 150
|
||||
Height = 23
|
||||
TabOrder = 1
|
||||
Text = 'H0STCNT0'
|
||||
TextHint = 'TR_ID ('#50696': H0STCNT0)'
|
||||
OnChange = edtTrIdChange
|
||||
end
|
||||
object edtTrKey: TEdit
|
||||
Left = 120
|
||||
Top = 89
|
||||
Width = 150
|
||||
Height = 23
|
||||
TabOrder = 2
|
||||
Text = '005930'
|
||||
TextHint = #51333#47785#53076#46300' ('#50696': 005930)'
|
||||
end
|
||||
object btnConnect: TButton
|
||||
Left = 560
|
||||
Top = 19
|
||||
Width = 170
|
||||
Height = 30
|
||||
Caption = #50672#44208
|
||||
TabOrder = 3
|
||||
OnClick = btnConnectClick
|
||||
end
|
||||
object btnSubscribe: TButton
|
||||
Left = 560
|
||||
Top = 55
|
||||
Width = 80
|
||||
Height = 30
|
||||
Caption = #44396#46021
|
||||
TabOrder = 4
|
||||
OnClick = btnSubscribeClick
|
||||
end
|
||||
object btnUnsubscribe: TButton
|
||||
Left = 650
|
||||
Top = 55
|
||||
Width = 80
|
||||
Height = 30
|
||||
Caption = #44396#46021#54644#51228
|
||||
TabOrder = 5
|
||||
OnClick = btnUnsubscribeClick
|
||||
end
|
||||
object btnDisconnect: TButton
|
||||
Left = 560
|
||||
Top = 91
|
||||
Width = 170
|
||||
Height = 30
|
||||
Caption = #50672#44208' '#54644#51228
|
||||
TabOrder = 6
|
||||
OnClick = btnDisconnectClick
|
||||
end
|
||||
object Memo1: TMemo
|
||||
Left = 24
|
||||
Top = 136
|
||||
Width = 706
|
||||
Height = 441
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -12
|
||||
Font.Name = 'Consolas'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
ScrollBars = ssVertical
|
||||
TabOrder = 7
|
||||
end
|
||||
object sgcWebSocketClient1: TsgcWebSocketClient
|
||||
Port = 80
|
||||
ConnectTimeout = 0
|
||||
ReadTimeout = -1
|
||||
WriteTimeout = 0
|
||||
TLS = False
|
||||
Proxy.Enabled = False
|
||||
Proxy.Port = 8080
|
||||
Proxy.ProxyType = pxyHTTP
|
||||
HeartBeat.Enabled = True
|
||||
HeartBeat.HeartBeatType = hbtAlways
|
||||
HeartBeat.Interval = 30000
|
||||
HeartBeat.Timeout = 0
|
||||
IPVersion = Id_IPv4
|
||||
OnConnect = sgcWebSocketClient1Connect
|
||||
OnMessage = sgcWebSocketClient1Message
|
||||
OnDisconnect = sgcWebSocketClient1Disconnect
|
||||
OnError = sgcWebSocketClient1Error
|
||||
Authentication.Enabled = False
|
||||
Authentication.URL.Enabled = True
|
||||
Authentication.Session.Enabled = False
|
||||
Authentication.Basic.Enabled = False
|
||||
Authentication.Token.Enabled = False
|
||||
Authentication.Token.AuthName = 'Bearer'
|
||||
Extensions.DeflateFrame.Enabled = False
|
||||
Extensions.DeflateFrame.WindowBits = 15
|
||||
Extensions.PerMessage_Deflate.Enabled = False
|
||||
Extensions.PerMessage_Deflate.ClientMaxWindowBits = 15
|
||||
Extensions.PerMessage_Deflate.ClientNoContextTakeOver = False
|
||||
Extensions.PerMessage_Deflate.MemLevel = 9
|
||||
Extensions.PerMessage_Deflate.ServerMaxWindowBits = 15
|
||||
Extensions.PerMessage_Deflate.ServerNoContextTakeOver = False
|
||||
Options.CleanDisconnect = False
|
||||
Options.FragmentedMessages = frgOnlyBuffer
|
||||
Options.Parameters = '/'
|
||||
Options.RaiseDisconnectExceptions = True
|
||||
Options.ValidateUTF8 = False
|
||||
Specifications.Drafts.Hixie76 = False
|
||||
Specifications.RFC6455 = True
|
||||
NotifyEvents = neAsynchronous
|
||||
LogFile.Enabled = False
|
||||
QueueOptions.Binary.Level = qmNone
|
||||
QueueOptions.Ping.Level = qmNone
|
||||
QueueOptions.Text.Level = qmNone
|
||||
WatchDog.Attempts = 0
|
||||
WatchDog.Enabled = False
|
||||
WatchDog.Interval = 10
|
||||
Throttle.BitsPerSec = 0
|
||||
Throttle.Enabled = False
|
||||
LoadBalancer.Enabled = False
|
||||
LoadBalancer.Port = 0
|
||||
LoadBalancer.TLS = False
|
||||
TLSOptions.VerifyCertificate = False
|
||||
TLSOptions.VerifyDepth = 0
|
||||
TLSOptions.Version = tlsUndefined
|
||||
TLSOptions.IOHandler = iohOpenSSL
|
||||
TLSOptions.OpenSSL_Options.APIVersion = oslAPI_1_0
|
||||
TLSOptions.OpenSSL_Options.LegacyProvider.Enabled = False
|
||||
TLSOptions.OpenSSL_Options.LegacyProvider.LibPath = oslpNone
|
||||
TLSOptions.OpenSSL_Options.LibPath = oslpNone
|
||||
TLSOptions.OpenSSL_Options.UnixSymLinks = oslsSymLinksDefault
|
||||
TLSOptions.OpenSSL_Options.VersionMin = tlsUndefined
|
||||
TLSOptions.OpenSSL_Options.X509Checks.Mode = []
|
||||
TLSOptions.SChannel_Options.CertStoreName = scsnMY
|
||||
TLSOptions.SChannel_Options.CertStorePath = scspStoreCurrentUser
|
||||
TLSOptions.SChannel_Options.UseLegacyCredentials = False
|
||||
Left = 400
|
||||
Top = 64
|
||||
end
|
||||
end
|
||||
306
한국투자증권(API)/legacy/websocket/delphi/MainForm.pas
Normal file
306
한국투자증권(API)/legacy/websocket/delphi/MainForm.pas
Normal file
@@ -0,0 +1,306 @@
|
||||
unit MainForm;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, System.JSON,
|
||||
Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.Dialogs,
|
||||
sgcWebSocket, sgcWebSocket_Classes, sgcWebSocket_Client, sgcBase_Classes,
|
||||
sgcSocket_Classes, sgcTCP_Classes, sgcWebSocket_Classes_Indy;
|
||||
|
||||
type
|
||||
TForm1 = class(TForm)
|
||||
sgcWebSocketClient1: TsgcWebSocketClient;
|
||||
btnConnect: TButton;
|
||||
btnSubscribe: TButton;
|
||||
btnUnsubscribe: TButton;
|
||||
btnDisconnect: TButton;
|
||||
Memo1: TMemo;
|
||||
edtApprovalKey: TEdit;
|
||||
edtTrId: TEdit;
|
||||
edtTrKey: TEdit;
|
||||
Label1: TLabel;
|
||||
Label2: TLabel;
|
||||
Label3: TLabel;
|
||||
procedure btnConnectClick(Sender: TObject);
|
||||
procedure btnSubscribeClick(Sender: TObject);
|
||||
procedure btnUnsubscribeClick(Sender: TObject);
|
||||
procedure btnDisconnectClick(Sender: TObject);
|
||||
procedure sgcWebSocketClient1Connect(Connection: TsgcWSConnection);
|
||||
procedure sgcWebSocketClient1Disconnect(Connection: TsgcWSConnection; Code: Integer);
|
||||
procedure sgcWebSocketClient1Message(Connection: TsgcWSConnection; const Text: string);
|
||||
procedure sgcWebSocketClient1Error(Connection: TsgcWSConnection; const Error: string);
|
||||
procedure FormCreate(Sender: TObject);
|
||||
procedure edtTrIdChange(Sender: TObject);
|
||||
private
|
||||
APPROVAL_KEY: string;
|
||||
procedure LogMessage(const Msg: string);
|
||||
procedure SendSubscription(const TrType: string);
|
||||
procedure UpdateWebSocketURL;
|
||||
public
|
||||
end;
|
||||
|
||||
var
|
||||
Form1: TForm1;
|
||||
|
||||
implementation
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
procedure TForm1.FormCreate(Sender: TObject);
|
||||
begin
|
||||
// 웹소켓 초기 설정
|
||||
sgcWebSocketClient1.Active := False;
|
||||
sgcWebSocketClient1.HeartBeat.Enabled := True;
|
||||
sgcWebSocketClient1.HeartBeat.Interval := 30000;
|
||||
|
||||
btnSubscribe.Enabled := False;
|
||||
btnUnsubscribe.Enabled := False;
|
||||
btnDisconnect.Enabled := False;
|
||||
|
||||
Memo1.Clear;
|
||||
|
||||
// 기본값 설정
|
||||
edtTrId.Text := 'H0STCNT0'; // 국내주식 체결통보
|
||||
edtTrKey.Text := '005930'; // 삼성전자
|
||||
|
||||
// URL 초기 설정
|
||||
UpdateWebSocketURL;
|
||||
|
||||
LogMessage('═══════════════════════════════════════════');
|
||||
LogMessage(' 한국투자증권 웹소켓 클라이언트 v1.0');
|
||||
LogMessage('═══════════════════════════════════════════');
|
||||
LogMessage('');
|
||||
LogMessage('[TR_ID 예시]');
|
||||
LogMessage(' H0STCNT0 : 국내주식 체결가');
|
||||
LogMessage(' H0STASP0 : 국내주식 호가');
|
||||
LogMessage(' HDFSCNT0 : 해외주식 지연체결가');
|
||||
LogMessage(' HDFSASP0 : 해외주식 호가');
|
||||
LogMessage('');
|
||||
LogMessage('[종목코드 예시]');
|
||||
LogMessage(' 국내: 005930 (삼성전자), 000660 (SK하이닉스)');
|
||||
LogMessage(' 해외: DNASTSLA (나스닥 테슬라), DNYSBABA (뉴욕 알리바바)');
|
||||
LogMessage('═══════════════════════════════════════════');
|
||||
end;
|
||||
|
||||
procedure TForm1.UpdateWebSocketURL;
|
||||
var
|
||||
trId: string;
|
||||
begin
|
||||
trId := Trim(edtTrId.Text);
|
||||
if trId <> '' then
|
||||
begin
|
||||
sgcWebSocketClient1.URL := 'ws://ops.koreainvestment.com:21000/tryitout/' + trId;
|
||||
LogMessage('URL 업데이트: ws://ops.koreainvestment.com:21000/tryitout/' + trId);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm1.edtTrIdChange(Sender: TObject);
|
||||
begin
|
||||
// TR_ID가 변경되면 URL도 자동 업데이트
|
||||
if not sgcWebSocketClient1.Active then
|
||||
UpdateWebSocketURL;
|
||||
end;
|
||||
|
||||
procedure TForm1.LogMessage(const Msg: string);
|
||||
begin
|
||||
Memo1.Lines.Add(FormatDateTime('[hh:nn:ss] ', Now) + Msg);
|
||||
if Memo1.Lines.Count > 0 then
|
||||
Memo1.Perform(EM_SCROLLCARET, 0, 0);
|
||||
end;
|
||||
|
||||
procedure TForm1.btnConnectClick(Sender: TObject);
|
||||
var
|
||||
trId: string;
|
||||
begin
|
||||
APPROVAL_KEY := Trim(edtApprovalKey.Text);
|
||||
trId := Trim(edtTrId.Text);
|
||||
|
||||
if APPROVAL_KEY = '' then
|
||||
begin
|
||||
ShowMessage('Approval Key를 입력해주세요.');
|
||||
edtApprovalKey.SetFocus;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if trId = '' then
|
||||
begin
|
||||
ShowMessage('TR_ID를 입력해주세요.');
|
||||
edtTrId.SetFocus;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
try
|
||||
// URL 최종 확인 및 업데이트
|
||||
UpdateWebSocketURL;
|
||||
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
LogMessage('웹소켓 연결 시도...');
|
||||
LogMessage('TR_ID: ' + trId);
|
||||
sgcWebSocketClient1.Active := True;
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
LogMessage('연결 실패: ' + E.Message);
|
||||
ShowMessage('연결 실패: ' + E.Message);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm1.btnDisconnectClick(Sender: TObject);
|
||||
begin
|
||||
if sgcWebSocketClient1.Active then
|
||||
begin
|
||||
LogMessage('연결 해제 중...');
|
||||
sgcWebSocketClient1.Active := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm1.SendSubscription(const TrType: string);
|
||||
var
|
||||
jHeader, jBody, jInput, jSend: TJSONObject;
|
||||
sJson: string;
|
||||
begin
|
||||
if not sgcWebSocketClient1.Active then
|
||||
begin
|
||||
ShowMessage('웹소켓이 연결되지 않았습니다.');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
jHeader := TJSONObject.Create;
|
||||
jInput := TJSONObject.Create;
|
||||
jBody := TJSONObject.Create;
|
||||
jSend := TJSONObject.Create;
|
||||
|
||||
try
|
||||
// Header 구성
|
||||
jHeader.AddPair('approval_key', APPROVAL_KEY);
|
||||
jHeader.AddPair('custtype', 'P');
|
||||
jHeader.AddPair('tr_type', TrType);
|
||||
jHeader.AddPair('content-type', 'utf-8');
|
||||
|
||||
// Input 구성
|
||||
jInput.AddPair('tr_id', 'H0STCNT0');
|
||||
jInput.AddPair('tr_key', '000660');
|
||||
|
||||
// Body 구성
|
||||
jBody.AddPair('input', jInput); // jInput 소유권은 jBody가 가져감
|
||||
|
||||
// 전체 JSON 구성
|
||||
jSend.AddPair('header', jHeader); // jHeader 소유권은 jSend가 가져감
|
||||
jSend.AddPair('body', jBody); // jBody 소유권은 jSend가 가져감
|
||||
|
||||
sJson := jSend.ToJSON;
|
||||
|
||||
LogMessage('전송 데이터: ' + sJson);
|
||||
sgcWebSocketClient1.WriteData(sJson);
|
||||
|
||||
if TrType = '1' then
|
||||
LogMessage('구독 요청 전송 완료')
|
||||
else
|
||||
LogMessage('구독 해제 요청 전송 완료');
|
||||
|
||||
finally
|
||||
// ✅ 하위 객체는 해제하지 말고, 최상위 객체(jSend)만 해제
|
||||
jSend.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm1.btnSubscribeClick(Sender: TObject);
|
||||
begin
|
||||
SendSubscription('1');
|
||||
end;
|
||||
|
||||
procedure TForm1.btnUnsubscribeClick(Sender: TObject);
|
||||
begin
|
||||
SendSubscription('2');
|
||||
end;
|
||||
|
||||
procedure TForm1.sgcWebSocketClient1Connect(Connection: TsgcWSConnection);
|
||||
begin
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
LogMessage('✓ 웹소켓 연결 성공!');
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
|
||||
btnConnect.Enabled := False;
|
||||
btnSubscribe.Enabled := True;
|
||||
btnUnsubscribe.Enabled := True;
|
||||
btnDisconnect.Enabled := True;
|
||||
edtApprovalKey.Enabled := False;
|
||||
|
||||
end;
|
||||
|
||||
procedure TForm1.sgcWebSocketClient1Disconnect(Connection: TsgcWSConnection; Code: Integer);
|
||||
begin
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
LogMessage('✗ 웹소켓 연결 해제됨');
|
||||
LogMessage('종료 코드: ' + IntToStr(Code));
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
|
||||
btnConnect.Enabled := True;
|
||||
btnSubscribe.Enabled := False;
|
||||
btnUnsubscribe.Enabled := False;
|
||||
btnDisconnect.Enabled := False;
|
||||
edtApprovalKey.Enabled := True;
|
||||
|
||||
end;
|
||||
|
||||
procedure TForm1.sgcWebSocketClient1Message(Connection: TsgcWSConnection; const Text: string);
|
||||
var
|
||||
jResponse: TJSONObject;
|
||||
jHeader, jBody: TJSONObject;
|
||||
trId, rtCd, msgCd, msg1: string;
|
||||
begin
|
||||
LogMessage('수신: ' + Text);
|
||||
|
||||
try
|
||||
jResponse := TJSONObject.ParseJSONValue(Text) as TJSONObject;
|
||||
if Assigned(jResponse) then
|
||||
try
|
||||
// Header 파싱
|
||||
if jResponse.TryGetValue<TJSONObject>('header', jHeader) then
|
||||
begin
|
||||
jHeader.TryGetValue<string>('tr_id', trId);
|
||||
LogMessage('TR_ID: ' + trId);
|
||||
end;
|
||||
|
||||
// Body 파싱
|
||||
if jResponse.TryGetValue<TJSONObject>('body', jBody) then
|
||||
begin
|
||||
// 응답 코드 확인
|
||||
rtCd := jBody.GetValue<string>('rt_cd', '');
|
||||
msgCd := jBody.GetValue<string>('msg_cd', '');
|
||||
msg1 := jBody.GetValue<string>('msg1', '');
|
||||
|
||||
if rtCd <> '' then
|
||||
begin
|
||||
if rtCd = '0' then
|
||||
LogMessage('✓ 성공: ' + msg1 + ' [' + msgCd + ']')
|
||||
else
|
||||
LogMessage('✗ 실패: ' + msg1 + ' [' + msgCd + ']');
|
||||
end
|
||||
else
|
||||
begin
|
||||
// 실시간 체결 데이터
|
||||
LogMessage('실시간 데이터: ' + jBody.ToJSON);
|
||||
end;
|
||||
end;
|
||||
|
||||
finally
|
||||
jResponse.Free;
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
LogMessage('메시지 파싱 오류: ' + E.Message);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TForm1.sgcWebSocketClient1Error(Connection: TsgcWSConnection; const Error: string);
|
||||
begin
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
LogMessage('❌ 에러 발생: ' + Error);
|
||||
LogMessage('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
ShowMessage('에러: ' + Error);
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user