=====
KOAPY
.. container::
.. image:: https://img.shields.io/pypi/v/koapy.svg
:target: https://pypi.python.org/pypi/koapy
:alt: PyPI Version
.. image:: https://img.shields.io/pypi/pyversions/koapy.svg
:target: https://pypi.python.org/pypi/koapy/
:alt: PyPI Python Versions
.. image:: https://img.shields.io/pypi/status/koapy.svg
:target: https://pypi.python.org/pypi/koapy/
:alt: PyPI Status
.. badges from below are commendted out
.. .. image:: https://img.shields.io/pypi/dm/koapy.svg
:target: https://pypi.python.org/pypi/koapy/
:alt: PyPI Monthly Donwloads
.. container::
.. image:: https://img.shields.io/github/workflow/status/elbakramer/koapy/CI/master
:target: https://github.com/elbakramer/koapy/actions/workflows/ci.yml
:alt: CI Build Status
.. .. image:: https://github.com/elbakramer/koapy/actions/workflows/ci.yml/badge.svg?branch=master
.. image:: https://img.shields.io/github/workflow/status/elbakramer/koapy/Documentation/master?label=docs
:target: https://elbakramer.github.io/koapy/
:alt: Documentation Build Status
.. .. image:: https://github.com/elbakramer/koapy/actions/workflows/documentation.yml/badge.svg?branch=master
.. image:: https://img.shields.io/codecov/c/github/elbakramer/koapy.svg
:target: https://codecov.io/gh/elbakramer/koapy
:alt: Codecov Coverage
.. .. image:: https://codecov.io/gh/elbakramer/koapy/branch/master/graph/badge.svg
.. image:: https://requires.io/github/elbakramer/koapy/requirements.svg?branch=master
:target: https://requires.io/github/elbakramer/koapy/requirements/?branch=master
:alt: Requires.io Requirements Status
.. badges from below are commendted out
.. .. image:: https://img.shields.io/travis/elbakramer/koapy.svg
:target: https://travis-ci.com/elbakramer/koapy
:alt: Travis CI Build Status
.. .. image:: https://travis-ci.com/elbakramer/koapy.svg?branch=master
.. .. image:: https://img.shields.io/readthedocs/koapy/latest.svg
:target: https://koapy.readthedocs.io/en/latest/?badge=latest
:alt: ReadTheDocs Documentation Build Status
.. .. image:: https://readthedocs.org/projects/koapy/badge/?version=latest
.. .. image:: https://pyup.io/repos/github/elbakramer/koapy/shield.svg
:target: https://pyup.io/repos/github/elbakramer/koapy/
:alt: PyUp Updates
.. container::
.. image:: https://img.shields.io/pypi/l/koapy.svg
:target: https://github.com/elbakramer/koapy/blob/master/LICENSE
:alt: PyPI License
.. badges from below are commendted out
.. .. image:: https://app.fossa.com/api/projects/git%2Bgithub.com%2Felbakramer%2Fkoapy.svg?type=shield
:target: https://app.fossa.com/projects/git%2Bgithub.com%2Felbakramer%2Fkoapy?ref=badge_shield
:alt: FOSSA Status
.. container::
.. image:: https://badges.gitter.im/elbakramer/koapy.svg
:target: https://gitter.im/koapy/community
:alt: Gitter Chat
.. .. image:: https://img.shields.io/gitter/room/elbakramer/koapy.svg
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code Style: Black
Kiwoom Open Api Plus Python
.. _MIT
: https://github.com/elbakramer/koapy/blob/master/LICENSE.MIT
.. _Apache-2.0
: https://github.com/elbakramer/koapy/blob/master/LICENSE.APACHE-2.0
.. _GPL-3.0-or-later
: https://github.com/elbakramer/koapy/blob/master/LICENSE.GPL-3.0-OR-LATER
Introduction
KOAPY ๋ ํค์์ฆ๊ถ์ OpenAPI+
_ ๋ฅผ Python_ ์์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋
๋ผ์ด๋ธ๋ฌ๋ฆฌ ํจํค์ง ๋ฐ ํด์
๋๋ค.
ํค์์์ ์ ๊ณตํ๋ OpenAPI+ ๋ฅผ ํ์ฉํ๋๋ฐ ํ์ํ ๊ตฌ์ฒด์ ์ธ ์ง์๋ค์ ์ ํ ์์ง ๋ชปํด๋,
๊ธฐ๋ณธ์ ์ธ Python_ ์ ๋ํ ์ง์๋ง ์ด๋ ์ ๋ ์๋ค๋ฉด ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ํ๋ ๊ฒ์
์ด์ ์ ๋์์ต๋๋ค.
์๋ฅผ ๋ค์ด Python_ ๊ธฐ์ค์ผ๋ก ์๋์ ๊ฐ์ ๋ด์ฉ๋ค์ ์ ๋ชจ๋ฅด๋๋ผ๋ ์ถฉ๋ถํ ๋ชจ๋ ๊ธฐ๋ฅ์
์ฌ์ฉํ ์ ์์์ ์๋ฏธํฉ๋๋ค.
- ํค์์์ ์ ๊ณตํ๋ OpenAPI+ ์ OCX ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌ์กฐ
- OCX ๋ฅผ Python_ ์์ ๊ตฌ๋ํ๊ธฐ ์ํด PyQt5_/PySide2_ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ |QAxWidget|_ ์์ฑ
- ์ปจํธ๋กค์์ ํจ์ ํธ์ถ์ ์ํด |dynamicCall|_ ํจ์ ์ฌ์ฉ
- ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ์ํด ์ ์ ํ |signal|/|slot| ์ค์ ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
.. _ํค์์ฆ๊ถ์ OpenAPI+
: https://www3.kiwoom.com/nkw.templateFrameSet.do?m=m1408000000
.. _Python: https://www.python.org/
.. _PyQt5: https://www.riverbankcomputing.com/software/pyqt/
.. _PySide2: https://doc.qt.io/qtforpython/index.html
.. |QAxWidget| replace:: QAxWidget
.. _QAxWidget: https://doc.qt.io/qt-5/qaxwidget.html
.. |dynamicCall| replace:: dynamicCall
.. _dynamicCall: https://doc.qt.io/qt-5/qaxbase.html#dynamicCall
.. |signal| replace:: signal
.. _signal: https://doc.qt.io/qt-5/signalsandslots.html#signals
.. |slot| replace:: slot
.. _slot: https://doc.qt.io/qt-5/signalsandslots.html#slots
Showcase
ํ
์ด๋ธ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์ํ pandas ์ํฌํธ
.. code-block:: python
>>> import pandas as pd
์ํธ๋ฆฌํฌ์ธํธ ๊ฐ์ฒด ์์ฑ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> from koapy import KiwoomOpenApiPlusEntrypoint
.. code-block:: python
>>> entrypoint = KiwoomOpenApiPlusEntrypoint()
ํค์์ฆ๊ถ ์๋ฒ์์ ์ฐ๊ฒฐ ํ์ธ ๋ฐ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
.. code-block:: python
>>> entrypoint.IsConnected()
False
>>> entrypoint.EnsureConnected()
True
>>> entrypoint.IsConnected()
True
์ข
๋ชฉ ๋ฆฌ์คํธ ๋ฐ ์ข
๋ชฉ ์ฝ๋์ ์ด๋ฆ ํ์ธ
.. code-block:: python
>>> code_list = entrypoint.GetCodeListByMarketAsList("0")
>>> code_list
['000020', '000040', '000050', '000060', '000070', ...]
.. code-block:: python
>>> name_list = [entrypoint.GetMasterCodeName(code) for code in code_list]
>>> name_list
['๋ํ์ฝํ', 'KR๋ชจํฐ์ค', '๊ฒฝ๋ฐฉ', '๋ฉ๋ฆฌ์ธ ํ์ฌ', '์ผ์ํ๋ฉ์ค', ...]
.. code-block:: python
>>> code_by_name = {name: code for code, name in zip(code_list, name_list)}
.. code-block:: python
>>> name = "์ผ์ฑ์ ์"
>>> code = code_by_name[name]
>>> code
'005930'
๋จ์ผ ์ข
๋ชฉ์ ๊ธฐ๋ณธ์ ๋ณด ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> info = entrypoint.GetStockBasicInfoAsDict(code)
>>> info
{'์ข
๋ชฉ์ฝ๋': '005930', '์ข
๋ชฉ๋ช
': '์ผ์ฑ์ ์', '๊ฒฐ์ฐ์': '12', '์ก๋ฉด๊ฐ': '100', '์๋ณธ๊ธ': '7780', '์์ฅ์ฃผ์': '5969783', '์ ์ฉ๋น์จ': '+0.12', '์ฐ์ค์ต๊ณ ': '+79800', '์ฐ์ค์ต์ ': '-71200', '์๊ฐ์ด์ก': '4417639', '์๊ฐ์ด์ก๋น์ค': '', '์ธ์ธ์์ง๋ฅ ': '+52.09', '๋์ฉ๊ฐ': '57170', 'PER': '19.27', 'EPS': '3841', 'ROE': '10.0', 'PBR': '1.88', 'EV': '5.09', 'BPS': '39406', '๋งค์ถ์ก': '2368070', '์์
์ด์ต': '359939', '๋น๊ธฐ์์ด์ต': '264078', '250์ต๊ณ ': '+86400', '250์ต์ ': '-68300', '์๊ฐ': '+74300', '๊ณ ๊ฐ': '+74600', '์ ๊ฐ': '+73400', '์ํ๊ฐ': '+95200', 'ํํ๊ฐ': '-51400', '๊ธฐ์ค๊ฐ': '73300', '์์์ฒด๊ฒฐ๊ฐ': '-0', '์์์ฒด๊ฒฐ์๋': '0', '250์ต๊ณ ๊ฐ์ผ': '20210202', '250์ต๊ณ ๊ฐ๋๋น์จ': '-14.35', '250์ต์ ๊ฐ์ผ': '20211013', '250์ต์ ๊ฐ๋๋น์จ': '+8.35', 'ํ์ฌ๊ฐ': '+74000', '๋๋น๊ธฐํธ': '2', '์ ์ผ๋๋น': '+700', '๋ฑ๋ฝ์จ': '+0.95', '๊ฑฐ๋๋': '12730034', '๊ฑฐ๋๋๋น': '-71.74', '์ก๋ฉด๊ฐ๋จ์': '์', '์ ํต์ฃผ์': '4459119', '์ ํต๋น์จ': '74.7'}
๋ณต์ ์ข
๋ชฉ์ ๊ธฐ๋ณธ์ ๋ณด ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> code_list_info = entrypoint.GetStockQuoteInfoAsDataFrame(code_list)
>>> code_list_info
์ข
๋ชฉ์ฝ๋ ์ข
๋ชฉ๋ช
ํ์ฌ๊ฐ ๊ธฐ์ค๊ฐ ์ ์ผ๋๋น ์ ์ผ๋๋น๊ธฐํธ ๋ฑ๋ฝ์จ \
0 000020 ๋ํ์ฝํ +12450 12300 +150 2 +1.22
1 000040 KR๋ชจํฐ์ค +810 790 +20 2 +2.53
2 000050 ๊ฒฝ๋ฐฉ +14450 14200 +250 2 +1.76
3 000060 ๋ฉ๋ฆฌ์ธ ํ์ฌ 49450 49450 0 3 0.00
4 000070 ์ผ์ํ๋ฉ์ค +90700 88400 +2300 2 +2.60
... ... ... ... ... ... ... ...
1744 580031 KB ์ธ๋ฒ์ค KOSDAQ150 ์ ๋ฌผ ETN -10930 10960 -30 5 -0.27
1745 580032 KB ๋ ๋ฒ๋ฆฌ์ง ๊ตฌ๋ฆฌ ์ ๋ฌผ ETN(H) -20515 20560 -45 5 -0.22
1746 580033 KB ์ธ๋ฒ์ค 2X ๊ตฌ๋ฆฌ ์ ๋ฌผ ETN(H) +18350 18280 +70 2 +0.38
1747 580010 KB Wise ๋ถํ ๋งค๋งค ETN +10820 10770 +50 2 +0.46
1748 590018 ๋ฏธ๋์์
์ค๊ตญ ์ฌ์ฒ 100 ETN +18810 18645 +165 2 +0.88
๊ฑฐ๋๋ ๊ฑฐ๋๋๊ธ ์ฒด๊ฒฐ๋ ... ELW๋ง๊ธฐ์ผ ๋ฏธ๊ฒฐ์ ์ฝ์ ๋ฏธ๊ฒฐ์ ์ ์ผ๋๋น ์ด๋ก ๊ฐ ๋ด์ฌ๋ณ๋์ฑ ๋ธํ ๊ฐ๋ง ์ํ ๋ฒ ๊ฐ \
0 135513 1670 +500 ... 00000000
1 230165 186 -10 ... 00000000
2 6214 89 97 ... 00000000
3 411584 20423 -4 ... 00000000
4 9052 813 +5 ... 00000000
... ... ... ... ... ... ... ... .. ... .. .. .. ..
1744 2 0 -1 ... 00000000
1745 0 0 ... 00000000
1746 17 0 +10 ... 00000000
1747 0 0 ... 00000000
1748 1 0 +1 ... 00000000
๋ก
0
1
2
3
4
... ..
1744
1745
1746
1747
1748
[1749 rows x 63 columns]
ํน์ ์ข
๋ชฉ์ ์ฐจํธ ๋ฐ์ดํฐ ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> chart_data = entrypoint.GetDailyStockDataAsDataFrame(code)
>>> chart_data
์ข
๋ชฉ์ฝ๋ ํ์ฌ๊ฐ ๊ฑฐ๋๋ ๊ฑฐ๋๋๊ธ ์ผ์ ์๊ฐ ๊ณ ๊ฐ ์ ๊ฐ ์์ ์ฃผ๊ฐ๊ตฌ๋ถ \
0 005930 74000 12730034 941413 20220204 74300 74600 73400
1 73300 17744721 1314506 20220203 74900 74900 73300
2 73300 21367447 1552586 20220128 71300 73700 71200
3 71300 22274777 1603685 20220127 73800 74000 71300
4 73300 12976730 955547 20220126 73900 74400 73100
... ... ... ... ... ... ... ... ... ...
9797 8010 4970 1 19850109 8240 8240 7950
9798 8300 12930 4 19850108 8400 8400 8300
9799 8410 11810 3 19850107 8400 8500 8390
9800 8390 1660 0 19850105 8400 8440 8390
9801 8450 1710 0 19850104 8500 8500 8450
์์ ๋น์จ ๋์
์ข
๊ตฌ๋ถ ์์
์ข
๊ตฌ๋ถ ์ข
๋ชฉ์ ๋ณด ์์ ์ฃผ๊ฐ์ด๋ฒคํธ ์ ์ผ์ข
๊ฐ
0
1
2
3
4
... ... ... ... ... ... ...
9797
9798
9799
9800
9801
[9802 rows x 15 columns]
ํค์์ฆ๊ถ์ TR ๋ฉํ ์ ๋ณด ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> from koapy import KiwoomOpenApiPlusTrInfo
.. code-block:: python
>>> tr_info_list = KiwoomOpenApiPlusTrInfo.get_trinfo_list()
.. code-block:: python
>>> data = pd.DataFrame.from_records([(info.tr_code, info.name) for info in tr_info_list], columns=['tr_code', 'name'])
>>> data
tr_code name
0 opt10001 ์ฃผ์๊ธฐ๋ณธ์ ๋ณด์์ฒญ
1 opt10059 ์ข
๋ชฉ๋ณํฌ์์๊ธฐ๊ด๋ณ์์ฒญ
2 opt10087 ์๊ฐ์ธ๋จ์ผ๊ฐ์์ฒญ
3 opt50037 ์ฝ์คํผ200์ง์์์ฒญ
4 opt90005 ํ๋ก๊ทธ๋จ๋งค๋งค์ถ์ด์์ฒญ
.. ... ...
220 opw20013 ๊ณ์ข๋ฏธ๊ฒฐ์ ์ฒญ์ฐ๊ฐ๋ฅ์๋์กฐํ์์ฒญ
221 opw20014 ์ ์ต์ค์๊ฐ์ฆ๊ฑฐ๊ธ์ฐ์ถ์์ฒญ
222 opw20015 ์ต์
๋งค๋์ฃผ๋ฌธ์ฆ๊ฑฐ๊ธํํฉ์์ฒญ
223 opw20016 ์ ์ฉ์ต์ ๊ฐ๋ฅ์ข
๋ชฉ์์ฒญ
224 opw20017 ์ ์ฉ์ต์ ๊ฐ๋ฅ๋ฌธ์
[225 rows x 2 columns]
OPT10001 TR ์์ฒญ ์ ์ก ๋ฐ ์๋ต ์ฒ๋ฆฌ (์ฑ๊ธ๋ฐ์ดํฐ)
.. code-block:: python
>>> opt10001_info = KiwoomOpenApiPlusTrInfo.get_trinfo_by_code("opt10001")
>>> opt10001_info
KiwoomOpenApiPlusTrInfo('opt10001', '์ฃผ์๊ธฐ๋ณธ์ ๋ณด์์ฒญ', 'STOCK', '', '1', '', [KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 6, 9001)], '์ฃผ์๊ธฐ๋ณธ์ ๋ณด', [KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 389), KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ๋ช
', 20, 50, 302), KiwoomOpenApiPlusTrInfo.Field('๊ฒฐ์ฐ์', 40, 20, 315), KiwoomOpenApiPlusTrInfo.Field('์ก๋ฉด๊ฐ', 60, 20, 310), KiwoomOpenApiPlusTrInfo.Field('์๋ณธ๊ธ', 80, 20, 309), KiwoomOpenApiPlusTrInfo.Field('์์ฅ์ฃผ์', 100, 20, 312), KiwoomOpenApiPlusTrInfo.Field('์ ์ฉ๋น์จ', 120, 20, 329), KiwoomOpenApiPlusTrInfo.Field('์ฐ์ค์ต๊ณ ', 140, 20, 1006), KiwoomOpenApiPlusTrInfo.Field('์ฐ์ค์ต์ ', 160, 20, 1009), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ์ด์ก', 180, 20, 311), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ์ด์ก๋น์ค', 200, 20, 336), KiwoomOpenApiPlusTrInfo.Field('์ธ์ธ์์ง๋ฅ ', 220, 20, 314), KiwoomOpenApiPlusTrInfo.Field('๋์ฉ๊ฐ', 240, 20, 308), KiwoomOpenApiPlusTrInfo.Field('PER', 260, 20, 1600), KiwoomOpenApiPlusTrInfo.Field('EPS', 280, 20, 1604), KiwoomOpenApiPlusTrInfo.Field('ROE', 300, 20, 1630), KiwoomOpenApiPlusTrInfo.Field('PBR', 320, 20, 1601), KiwoomOpenApiPlusTrInfo.Field('EV', 340, 20, 1608), KiwoomOpenApiPlusTrInfo.Field('BPS', 360, 20, 1605), KiwoomOpenApiPlusTrInfo.Field('๋งค์ถ์ก', 380, 20, 1610), KiwoomOpenApiPlusTrInfo.Field('์์
์ด์ต', 400, 20, 1611), KiwoomOpenApiPlusTrInfo.Field('๋น๊ธฐ์์ด์ต', 420, 20, 1614), KiwoomOpenApiPlusTrInfo.Field('250์ต๊ณ ', 440, 20, 1000), KiwoomOpenApiPlusTrInfo.Field('250์ต์ ', 460, 20, 1003), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ', 480, 20, 16), KiwoomOpenApiPlusTrInfo.Field('๊ณ ๊ฐ', 500, 20, 17), KiwoomOpenApiPlusTrInfo.Field('์ ๊ฐ', 520, 20, 18), KiwoomOpenApiPlusTrInfo.Field('์ํ๊ฐ', 540, 20, 305), KiwoomOpenApiPlusTrInfo.Field('ํํ๊ฐ', 560, 20, 306), KiwoomOpenApiPlusTrInfo.Field('๊ธฐ์ค๊ฐ', 580, 20, 307), KiwoomOpenApiPlusTrInfo.Field('์์์ฒด๊ฒฐ๊ฐ', 600, 20, 10023), KiwoomOpenApiPlusTrInfo.Field('์์์ฒด๊ฒฐ์๋', 620, 20, 10024), KiwoomOpenApiPlusTrInfo.Field('250์ต๊ณ ๊ฐ์ผ', 640, 20, 1001), KiwoomOpenApiPlusTrInfo.Field('250์ต๊ณ ๊ฐ๋๋น์จ', 660, 20, 1002), KiwoomOpenApiPlusTrInfo.Field('250์ต์ ๊ฐ์ผ', 680, 20, 1004), KiwoomOpenApiPlusTrInfo.Field('250์ต์ ๊ฐ๋๋น์จ', 700, 20, 1005), KiwoomOpenApiPlusTrInfo.Field('ํ์ฌ๊ฐ', 720, 20, 10), KiwoomOpenApiPlusTrInfo.Field('๋๋น๊ธฐํธ', 740, 20, 25), KiwoomOpenApiPlusTrInfo.Field('์ ์ผ๋๋น', 760, 20, 11), KiwoomOpenApiPlusTrInfo.Field('๋ฑ๋ฝ์จ', 780, 20, 12), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋', 800, 20, 13), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋๋น', 820, 20, 30), KiwoomOpenApiPlusTrInfo.Field('์ก๋ฉด๊ฐ๋จ์', 840, 20, 796), KiwoomOpenApiPlusTrInfo.Field('์ ํต์ฃผ์', 840, 20, 1683), KiwoomOpenApiPlusTrInfo.Field('์ ํต๋น์จ', 840, 20, 1684)], '', [])
>>> opt10001_info.inputs
[KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 6, 9001)]
>>> opt10001_info.single_outputs
[KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 389), KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ๋ช
', 20, 50, 302), KiwoomOpenApiPlusTrInfo.Field('๊ฒฐ์ฐ์', 40, 20, 315), KiwoomOpenApiPlusTrInfo.Field('์ก๋ฉด๊ฐ', 60, 20, 310), KiwoomOpenApiPlusTrInfo.Field('์๋ณธ๊ธ', 80, 20, 309), KiwoomOpenApiPlusTrInfo.Field('์์ฅ์ฃผ์', 100, 20, 312), KiwoomOpenApiPlusTrInfo.Field('์ ์ฉ๋น์จ', 120, 20, 329), KiwoomOpenApiPlusTrInfo.Field('์ฐ์ค์ต๊ณ ', 140, 20, 1006), KiwoomOpenApiPlusTrInfo.Field('์ฐ์ค์ต์ ', 160, 20, 1009), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ์ด์ก', 180, 20, 311), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ์ด์ก๋น์ค', 200, 20, 336), KiwoomOpenApiPlusTrInfo.Field('์ธ์ธ์์ง๋ฅ ', 220, 20, 314), KiwoomOpenApiPlusTrInfo.Field('๋์ฉ๊ฐ', 240, 20, 308), KiwoomOpenApiPlusTrInfo.Field('PER', 260, 20, 1600), KiwoomOpenApiPlusTrInfo.Field('EPS', 280, 20, 1604), KiwoomOpenApiPlusTrInfo.Field('ROE', 300, 20, 1630), KiwoomOpenApiPlusTrInfo.Field('PBR', 320, 20, 1601), KiwoomOpenApiPlusTrInfo.Field('EV', 340, 20, 1608), KiwoomOpenApiPlusTrInfo.Field('BPS', 360, 20, 1605), KiwoomOpenApiPlusTrInfo.Field('๋งค์ถ์ก', 380, 20, 1610), KiwoomOpenApiPlusTrInfo.Field('์์
์ด์ต', 400, 20, 1611), KiwoomOpenApiPlusTrInfo.Field('๋น๊ธฐ์์ด์ต', 420, 20, 1614), KiwoomOpenApiPlusTrInfo.Field('250์ต๊ณ ', 440, 20, 1000), KiwoomOpenApiPlusTrInfo.Field('250์ต์ ', 460, 20, 1003), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ', 480, 20, 16), KiwoomOpenApiPlusTrInfo.Field('๊ณ ๊ฐ', 500, 20, 17), KiwoomOpenApiPlusTrInfo.Field('์ ๊ฐ', 520, 20, 18), KiwoomOpenApiPlusTrInfo.Field('์ํ๊ฐ', 540, 20, 305), KiwoomOpenApiPlusTrInfo.Field('ํํ๊ฐ', 560, 20, 306), KiwoomOpenApiPlusTrInfo.Field('๊ธฐ์ค๊ฐ', 580, 20, 307), KiwoomOpenApiPlusTrInfo.Field('์์์ฒด๊ฒฐ๊ฐ', 600, 20, 10023), KiwoomOpenApiPlusTrInfo.Field('์์์ฒด๊ฒฐ์๋', 620, 20, 10024), KiwoomOpenApiPlusTrInfo.Field('250์ต๊ณ ๊ฐ์ผ', 640, 20, 1001), KiwoomOpenApiPlusTrInfo.Field('250์ต๊ณ ๊ฐ๋๋น์จ', 660, 20, 1002), KiwoomOpenApiPlusTrInfo.Field('250์ต์ ๊ฐ์ผ', 680, 20, 1004), KiwoomOpenApiPlusTrInfo.Field('250์ต์ ๊ฐ๋๋น์จ', 700, 20, 1005), KiwoomOpenApiPlusTrInfo.Field('ํ์ฌ๊ฐ', 720, 20, 10), KiwoomOpenApiPlusTrInfo.Field('๋๋น๊ธฐํธ', 740, 20, 25), KiwoomOpenApiPlusTrInfo.Field('์ ์ผ๋๋น', 760, 20, 11), KiwoomOpenApiPlusTrInfo.Field('๋ฑ๋ฝ์จ', 780, 20, 12), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋', 800, 20, 13), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋๋น', 820, 20, 30), KiwoomOpenApiPlusTrInfo.Field('์ก๋ฉด๊ฐ๋จ์', 840, 20, 796), KiwoomOpenApiPlusTrInfo.Field('์ ํต์ฃผ์', 840, 20, 1683), KiwoomOpenApiPlusTrInfo.Field('์ ํต๋น์จ', 840, 20, 1684)]
>>> opt10001_info.multi_outputs
[]
.. code-block:: python
>>> request_name = "์ฃผ์๊ธฐ๋ณธ์ ๋ณด์์ฒญ" # ์ฌ์ฉ์ ๊ตฌ๋ถ๋ช
, ๊ตฌ๋ถ๊ฐ๋ฅํ ์์์ ๋ฌธ์์ด
>>> tr_code = "opt10001"
>>> screen_no = "0001" # ํ๋ฉด๋ฒํธ, 0000 ์ ์ ์ธํ 4์๋ฆฌ ์ซ์ ์์๋ก ์ง์ , None ์ ๊ฒฝ์ฐ ๋ด๋ถ์ ์ผ๋ก ํ๋ฉด๋ฒํธ ์๋ํ ๋น
>>> inputs = {
... "์ข
๋ชฉ์ฝ๋": "005930", # ์ผ์ฑ์ ์ ์ข
๋ชฉ์ฝ๋
... }
>>> output = {}
>>> for event in entrypoint.TransactionCall(request_name, tr_code, screen_no, inputs):
... names = event.single_data.names
... values = event.single_data.values
... for name, value in zip(names, values):
... output[name] = value
>>> output
{'์ข
๋ชฉ์ฝ๋': '005930', '์ข
๋ชฉ๋ช
': '์ผ์ฑ์ ์', '๊ฒฐ์ฐ์': '12', '์ก๋ฉด๊ฐ': '100', '์๋ณธ๊ธ': '7780', '์์ฅ์ฃผ์': '5969783', '์ ์ฉ๋น์จ': '+0.12', '์ฐ์ค์ต๊ณ ': '+79800', '์ฐ์ค์ต์ ': '-71200', '์๊ฐ์ด์ก': '4417639', '์๊ฐ์ด์ก๋น์ค': '', '์ธ์ธ์์ง๋ฅ ': '+52.09', '๋์ฉ๊ฐ': '57170', 'PER': '19.27', 'EPS': '3841', 'ROE': '10.0', 'PBR': '1.88', 'EV': '5.09', 'BPS': '39406', '๋งค์ถ์ก': '2368070', '์์
์ด์ต': '359939', '๋น๊ธฐ์์ด์ต': '264078', '250์ต๊ณ ': '+86400', '250์ต์ ': '-68300', '์๊ฐ': '+74300', '๊ณ ๊ฐ': '+74600', '์ ๊ฐ': '+73400', '์ํ๊ฐ': '+95200', 'ํํ๊ฐ': '-51400', '๊ธฐ์ค๊ฐ': '73300', '์์์ฒด๊ฒฐ๊ฐ': '-0', '์์์ฒด๊ฒฐ์๋': '0', '250์ต๊ณ ๊ฐ์ผ': '20210202', '250์ต๊ณ ๊ฐ๋๋น์จ': '-14.35', '250์ต์ ๊ฐ์ผ': '20211013', '250์ต์ ๊ฐ๋๋น์จ': '+8.35', 'ํ์ฌ๊ฐ': '+74000', '๋๋น๊ธฐํธ': '2', '์ ์ผ๋๋น': '+700', '๋ฑ๋ฝ์จ': '+0.95', '๊ฑฐ๋๋': '12730034', '๊ฑฐ๋๋๋น': '-71.74', '์ก๋ฉด๊ฐ๋จ์': '์', '์ ํต์ฃผ์': '4459119', '์ ํต๋น์จ': '74.7'}
OPT10081 TR ์์ฒญ ์ ์ก ๋ฐ ์๋ต ์ฒ๋ฆฌ (๋ฉํฐ๋ฐ์ดํฐ)
.. code-block:: python
>>> opt10081_info = KiwoomOpenApiPlusTrInfo.get_trinfo_by_code("opt10081")
>>> opt10081_info
KiwoomOpenApiPlusTrInfo('opt10081', '์ฃผ์์ผ๋ด์ฐจํธ์กฐํ์์ฒญ', 'SCHART', '', '1', '3003', [KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 9001), KiwoomOpenApiPlusTrInfo.Field('๊ธฐ์ค์ผ์', 20, 20, 9004), KiwoomOpenApiPlusTrInfo.Field('์์ ์ฃผ๊ฐ๊ตฌ๋ถ', 40, 20, 9055)], '์ฃผ์์ผ๋ด์ฐจํธ', [KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 9001)], '์ฃผ์์ผ๋ด์ฐจํธ์กฐํ', [KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 9001), KiwoomOpenApiPlusTrInfo.Field('ํ์ฌ๊ฐ', 0, 20, 10), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋', 40, 20, 13), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋๊ธ', 60, 20, 14), KiwoomOpenApiPlusTrInfo.Field('์ผ์', 80, 20, 22), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ', 100, 20, 16), KiwoomOpenApiPlusTrInfo.Field('๊ณ ๊ฐ', 120, 20, 17), KiwoomOpenApiPlusTrInfo.Field('์ ๊ฐ', 140, 20, 18), KiwoomOpenApiPlusTrInfo.Field('์์ ์ฃผ๊ฐ๊ตฌ๋ถ', 160, 20, 3502), KiwoomOpenApiPlusTrInfo.Field('์์ ๋น์จ', 180, 20, 3503), KiwoomOpenApiPlusTrInfo.Field('๋์
์ข
๊ตฌ๋ถ', 200, 20, 317), KiwoomOpenApiPlusTrInfo.Field('์์
์ข
๊ตฌ๋ถ', 220, 20, 318), KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ ๋ณด', 240, 20, 370), KiwoomOpenApiPlusTrInfo.Field('์์ ์ฃผ๊ฐ์ด๋ฒคํธ', 260, 20, 3501), KiwoomOpenApiPlusTrInfo.Field('์ ์ผ์ข
๊ฐ', 280, 20, 346)])
>>> opt10081_info.inputs
[KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 9001), KiwoomOpenApiPlusTrInfo.Field('๊ธฐ์ค์ผ์', 20, 20, 9004), KiwoomOpenApiPlusTrInfo.Field('์์ ์ฃผ๊ฐ๊ตฌ๋ถ', 40, 20, 9055)]
>>> opt10081_info.single_outputs
[KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 9001)]
>>> opt10081_info.multi_outputs
[KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ฝ๋', 0, 20, 9001), KiwoomOpenApiPlusTrInfo.Field('ํ์ฌ๊ฐ', 0, 20, 10), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋', 40, 20, 13), KiwoomOpenApiPlusTrInfo.Field('๊ฑฐ๋๋๊ธ', 60, 20, 14), KiwoomOpenApiPlusTrInfo.Field('์ผ์', 80, 20, 22), KiwoomOpenApiPlusTrInfo.Field('์๊ฐ', 100, 20, 16), KiwoomOpenApiPlusTrInfo.Field('๊ณ ๊ฐ', 120, 20, 17), KiwoomOpenApiPlusTrInfo.Field('์ ๊ฐ', 140, 20, 18), KiwoomOpenApiPlusTrInfo.Field('์์ ์ฃผ๊ฐ๊ตฌ๋ถ', 160, 20, 3502), KiwoomOpenApiPlusTrInfo.Field('์์ ๋น์จ', 180, 20, 3503), KiwoomOpenApiPlusTrInfo.Field('๋์
์ข
๊ตฌ๋ถ', 200, 20, 317), KiwoomOpenApiPlusTrInfo.Field('์์
์ข
๊ตฌ๋ถ', 220, 20, 318), KiwoomOpenApiPlusTrInfo.Field('์ข
๋ชฉ์ ๋ณด', 240, 20, 370), KiwoomOpenApiPlusTrInfo.Field('์์ ์ฃผ๊ฐ์ด๋ฒคํธ', 260, 20, 3501), KiwoomOpenApiPlusTrInfo.Field('์ ์ผ์ข
๊ฐ', 280, 20, 346)]
.. code-block:: python
>>> import datetime
>>> date = datetime.datetime.now().strftime("%Y%m%d")
>>> date
'20220206'
.. code-block:: python
>>> request_name = "์ฃผ์์ผ๋ด์ฐจํธ์กฐํ์์ฒญ" # ์ฌ์ฉ์ ๊ตฌ๋ถ๋ช
, ๊ตฌ๋ถ๊ฐ๋ฅํ ์์์ ๋ฌธ์์ด
>>> tr_code = "opt10081"
>>> screen_no = "0001" # ํ๋ฉด๋ฒํธ, 0000 ์ ์ ์ธํ 4์๋ฆฌ ์ซ์ ์์๋ก ์ง์ , None ์ ๊ฒฝ์ฐ ๋ด๋ถ์ ์ผ๋ก ํ๋ฉด๋ฒํธ ์๋ํ ๋น
>>> inputs = {
... "์ข
๋ชฉ์ฝ๋": "005930", # ์ผ์ฑ์ ์ ์ข
๋ชฉ์ฝ๋
... "๊ธฐ์ค์ผ์": "20220206", # ๊ฐ์ฅ ์ต๊ทผ ๋ ์ง์ YYYYMMDD ํฌ๋งท
... "์์ ์ฃผ๊ฐ๊ตฌ๋ถ": "0", # 0:์ผ๋ฐ์ฃผ๊ฐ, 1:์์ ์ฃผ๊ฐ
... }
>>> data_list = []
>>> for event in entrypoint.TransactionCall(request_name, tr_code, screen_no, inputs):
... columns = event.multi_data.names
... records = [values.values for values in event.multi_data.values]
... data = pd.DataFrame.from_records(records, columns=columns)
... data_list.append(data)
>>> data = pd.concat(data_list, axis=0).reset_index(drop=True)
>>> data
์ข
๋ชฉ์ฝ๋ ํ์ฌ๊ฐ ๊ฑฐ๋๋ ๊ฑฐ๋๋๊ธ ์ผ์ ์๊ฐ ๊ณ ๊ฐ ์ ๊ฐ ์์ ์ฃผ๊ฐ๊ตฌ๋ถ \
0 005930 74000 12730034 941413 20220204 74300 74600 73400
1 73300 17744721 1314506 20220203 74900 74900 73300
2 73300 21367447 1552586 20220128 71300 73700 71200
3 71300 22274777 1603685 20220127 73800 74000 71300
4 73300 12976730 955547 20220126 73900 74400 73100
... ... ... ... ... ... ... ... ... ...
9797 8010 4970 1 19850109 8240 8240 7950
9798 8300 12930 4 19850108 8400 8400 8300
9799 8410 11810 3 19850107 8400 8500 8390
9800 8390 1660 0 19850105 8400 8440 8390
9801 8450 1710 0 19850104 8500 8500 8450
์์ ๋น์จ ๋์
์ข
๊ตฌ๋ถ ์์
์ข
๊ตฌ๋ถ ์ข
๋ชฉ์ ๋ณด ์์ ์ฃผ๊ฐ์ด๋ฒคํธ ์ ์ผ์ข
๊ฐ
0
1
2
3
4
... ... ... ... ... ... ...
9797
9798
9799
9800
9801
[9802 rows x 15 columns]
์กฐ๊ฑด๊ฒ์ ์กฐ๊ฑด์ ์ค์ ๋ถ๋ฌ์ค๊ธฐ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> entrypoint.IsConditionLoaded()
False
>>> entrypoint.EnsureConditionLoaded()
1
>>> entrypoint.IsConditionLoaded()
True
๋ถ๋ฌ์จ ์กฐ๊ฑด์ ๋ชฉ๋ก ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> condition_name_list = entrypoint.GetConditionNameListAsList()
>>> condition_name_list
[(0, '๋ํ ์ ํ๊ฐ ์ฐ๋์ฃผ'), (1, '์ค์ํ ์ ํ๊ฐ์ฃผ')]
์กฐ๊ฑด์ ๋จ์ ๊ฒ์
~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> condition_name = "๋ํ ์ ํ๊ฐ ์ฐ๋์ฃผ"
>>> condition_met_code_list, condition_met_code_list_info = entrypoint.GetCodeListByCondition(condition_name, with_info=True)
>>> condition_met_code_list
['000240', '001800', '001880', '003230', '003550', '004000', '006040', '006390', '006650', '007700', '009970', '011780', '014830', '020000', '021240', '025540', '030520', '033290', '033780', '036830', '042420', '056190', '057050', '060150', '064960', '069080', '081660', '095660', '096530', '110790', '111770', '137310', '161390', '161890', '185750', '192080', '192400', '200130', '243070', '271560', '284740', '285130', '294870', '300720', '950130']
>>> condition_met_code_list_info # same as entrypoint.GetStockQuoteInfoAsDataFrame(condition_met_code_list)
์ข
๋ชฉ์ฝ๋ ์ข
๋ชฉ๋ช
ํ์ฌ๊ฐ ๊ธฐ์ค๊ฐ ์ ์ผ๋๋น ์ ์ผ๋๋น๊ธฐํธ ๋ฑ๋ฝ์จ ๊ฑฐ๋๋ ๊ฑฐ๋๋๊ธ \
0 000240 ํ๊ตญ์ค์ปดํผ๋ +13500 13400 +100 2 +0.75 56484 760
1 001800 ์ค๋ฆฌ์จํ๋ฉ์ค +14600 14250 +350 2 +2.46 69827 1008
2 001880 DL๊ฑด์ค -27950 28050 -100 5 -0.36 20925 583
3 003230 ์ผ์์ํ +91300 90000 +1300 2 +1.44 58660 5289
4 003550 LG +76300 74600 +1700 2 +2.28 249415 18925
.. ... ... ... ... ... ... ... ... ...
40 284740 ์ฟ ์ฟ ํ์์ค +36700 35900 +800 2 +2.23 30134 1102
41 285130 SK์ผ๋ฏธ์นผ +130500 129500 +1000 2 +0.77 58032 7544
42 294870 HDCํ๋์ฐ์
๊ฐ๋ฐ +15600 14600 +1000 2 +6.85 3145356 49106
43 300720 ํ์ผ์๋ฉํธ +19250 18750 +500 2 +2.67 159419 3021
44 950130 ์์ธ์ค๋ฐ์ด์ค -18400 22400 -4000 5 -17.86 5877689 119649
์ฒด๊ฒฐ๋ ... ELW๋ง๊ธฐ์ผ ๋ฏธ๊ฒฐ์ ์ฝ์ ๋ฏธ๊ฒฐ์ ์ ์ผ๋๋น ์ด๋ก ๊ฐ ๋ด์ฌ๋ณ๋์ฑ ๋ธํ ๊ฐ๋ง ์ํ ๋ฒ ๊ฐ ๋ก
0 5549 ... 00000000
1 +1 ... 00000000
2 369 ... 00000000
3 +2 ... 00000000
4 +100 ... 00000000
.. ... ... ... ... ... .. ... .. .. .. .. ..
40 -3 ... 00000000
41 -1 ... 00000000
42 -25 ... 00000000
43 -4 ... 00000000
44 -36 ... 00000000
[45 rows x 63 columns]
GRPC ์คํธ๋ฆผ ์ฒ๋ฆฌ ๊ด๋ จ ์ ํธ๋ฆฌํฐ ํจ์ (๋ฐ๋ชจ ๋ชฉ์ )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> import contextlib
>>> import threading
>>> import warnings
>>> import grpc
>>> @contextlib.contextmanager
... def warn_on_rpc_error_context():
... try:
... yield
... except grpc.RpcError as e:
... warnings.warn(str(e))
>>> def warn_on_rpc_error(stream):
... with warn_on_rpc_error_context():
... for event in stream:
... yield event
>>> def cancel_after(stream, after):
... timer = threading.Timer(after, stream.cancel)
... timer.start()
... return warn_on_rpc_error(stream)
์กฐ๊ฑด์ ์ค์๊ฐ ๊ฒ์
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> condition_name = "์ค์ํ ์ ํ๊ฐ์ฃผ"
>>> stream = entrypoint.GetCodeListByConditionAsStream(condition_name)
>>> condition_met_code_list = []
>>> data_list = []
>>> for event in cancel_after(stream, 10):
... if event.name == "OnReceiveTrCondition":
... initially_included_code_list = event.arguments[1].string_value
... initially_included_code_list = initially_included_code_list.rstrip(';').split(';') if initially_included_code_list else []
... condition_met_code_list.extend(initially_included_code_list)
... elif event.name == "OnReceiveRealCondition":
... code = event.arguments[0].string_value
... condition_type = event.arguments[1].string_value
... if condition_type == "I":
... code_inserted = code
... condition_met_code_list.append(code_inserted)
... elif condition_type == "D":
... code_deleted = code
... condition_met_code_list.remove(code_deleted)
... elif event.name == "OnReceiveTrData":
... columns = event.multi_data.names
... records = [values.values for values in event.multi_data.values]
... data = pd.DataFrame.from_records(records, columns=columns)
... data_list.append(data)
>>> condition_met_code_list
['900290', '900310', '900340', '002170', '017890', '023600', '036190', '037710', '049430', '073560', '140910', '187870', '192440', '210540', '225220', '263690', '352700', '950190', '900280', '900250']
>>> data_list
[]
๊ณ์ข์ ๋ณด ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> account_list = entrypoint.GetAccountList()
>>> account_list
['8014526011']
>>> first_account_no = entrypoint.GetFirstAvailableAccount()
>>> first_account_no
'8014526011'
์ฃผ๋ฌธ ์์ฒญ (์ผ์ฑ์ ์ ์์ฅ๊ฐ ๋งค์)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> request_name = "์ผ์ฑ์ ์ 1์ฃผ ์์ฅ๊ฐ ์ ๊ท ๋งค์" # ์ฌ์ฉ์ ๊ตฌ๋ถ๋ช
, ๊ตฌ๋ถ๊ฐ๋ฅํ ์์์ ๋ฌธ์์ด
>>> screen_no = "0001" # ํ๋ฉด๋ฒํธ, 0000 ์ ์ ์ธํ 4์๋ฆฌ ์ซ์ ์์๋ก ์ง์ , None ์ ๊ฒฝ์ฐ ๋ด๋ถ์ ์ผ๋ก ํ๋ฉด๋ฒํธ ์๋ํ ๋น
>>> account_no = "8014526011" # ๊ณ์ข๋ฒํธ 10์๋ฆฌ, ์ฌ๊ธฐ์๋ ๊ณ์ข๋ฒํธ ๋ชฉ๋ก์์ ์ฒซ๋ฒ์งธ๋ก ๋ฐ๊ฒฌํ ๊ณ์ข๋ฒํธ๋ก ๋งค์์ฒ๋ฆฌ
>>> order_type = 1 # ์ฃผ๋ฌธ์ ํ, 1:์ ๊ท๋งค์
>>> code = "005930" # ์ข
๋ชฉ์ฝ๋, ์์ ์ผ์ฑ์ ์ ์ข
๋ชฉ์ฝ๋
>>> quantity = 1 # ์ฃผ๋ฌธ์๋, 1์ฃผ ๋งค์
>>> price = 0 # ์ฃผ๋ฌธ๊ฐ๊ฒฉ, ์์ฅ๊ฐ ๋งค์๋ ๊ฐ๊ฒฉ ์ค์ ์๋ฏธ ์์ผ๋ฏ๋ก ๊ธฐ๋ณธ๊ฐ 0 ์ผ๋ก ์ค์
>>> quote_type = "03" # ๊ฑฐ๋๊ตฌ๋ถ, 03:์์ฅ๊ฐ
>>> original_order_no = "" # ์์ฃผ๋ฌธ๋ฒํธ, ์ฃผ๋ฌธ ์ ์ /์ทจ์ ๋ฑ์์ ์ฌ์ฉ
>>> stream = entrypoint.OrderCall(request_name, screen_no, account_no, order_type, code, quantity, price, quote_type, original_order_no)
>>> for event in warn_on_rpc_error(stream):
... if event.name == "OnReceiveTrData":
... order_no = event.single_data.values[0]
... elif event.name == "OnReceiveChejanData":
... gubun = event.arguments[0].string_value
... data = dict(zip(event.single_data.names, event.single_data.values))
... if gubun == "0":
... status = data["์ฃผ๋ฌธ์ํ"]
... if status == "์ ์":
... pass
... elif status == "์ฒด๊ฒฐ":
... orders_filled = data["์ฒด๊ฒฐ๋"]
... orders_left = data["๋ฏธ์ฒด๊ฒฐ์๋"]
... elif status == "ํ์ธ":
... org_order_no = data["์์ฃผ๋ฌธ๋ฒํธ"]
... assert original_order_no == org_order_no
... elif gubun in ["1", "4"]:
... stocks = data["๋ณด์ ์๋"]
ํค์์ฆ๊ถ์ ์ค์๊ฐ ๋ฐ์ดํฐ ๋ฉํ ์ ๋ณด ํ์ธ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
>>> from koapy import KiwoomOpenApiPlusRealType
.. code-block:: python
>>> realtype_list = KiwoomOpenApiPlusRealType.get_realtype_info_list()
.. code-block:: python
>>> realtype_descs = [realtype.desc for realtype in realtype_list]
>>> realtype_descs
['์ฃผ์์์ธ', '์ฃผ์์ฒด๊ฒฐ', '์ฃผ์์ํํ', '์ฃผ์์ฐ์ ํธ๊ฐ', '์ฃผ์ํธ๊ฐ์๋', '์ฃผ์์๊ฐ์ธํธ๊ฐ', '์ฃผ์๋น์ผ๊ฑฐ๋์', 'ETF NAV', 'ELW ์งํ', 'ELW ์ด๋ก ๊ฐ', '์ฃผ์์์์ฒด๊ฒฐ', '์ฃผ์์ข
๋ชฉ์ ๋ณด', '์์์ฐ์ฅ์ ๋ณด', 'ECN์ฃผ์์์ธ', 'ECN์ฃผ์์ฒด๊ฒฐ', 'ECN์ฃผ์์ฐ์ ํธ๊ฐ', 'ECN์ฃผ์ํธ๊ฐ์๋', 'ECN์ฃผ์์๊ฐ์ธํธ๊ฐ', 'ECN์ฃผ์๋น์ผ๊ฑฐ๋์', '์๊ฐ์ธ์ข
๋ชฉ์ ๋ณด', '์ฃผ์๊ฑฐ๋์', '์ฃผ์๊ฑฐ๋์(1LINE)', '์ข
๋ชฉ๋ณํ๋ก๊ทธ๋จ๋งค๋งค', 'VI์ ์ ์์๊ฐ', 'VI๋ฐ๋/ํด์ -์ข
๋ชฉ๋ณ', '์ข
๋ชฉ๋ณํ๋ก๊ทธ๋จ๋งค๋งค2', '์ข
๋ชฉํฌ์์(์ ์ )', '์ข
๋ชฉํฌ์์(๊ฑฐ๋์)', '๋์ฃผ๊ฐ๋ฅ์๋', '์ ๋ฌผ์ต์
์ฐ์ ํธ๊ฐ', '์ ๋ฌผ์์ธ', '์ ๋ฌผํธ๊ฐ์๋', '์ ๋ฌผ์ด๋ก ๊ฐ', '์ ๋ฌผ๊ธฐ์ด์์ฐ์์ธ', '์ค์๊ฐ์ํํ๊ฐ', '์ต์
์์ธ', '์ต์
ํธ๊ฐ์๋', '์ต์
์ด๋ก ๊ฐ', '์ฃผ์์ต์
์์ธ', '์ฃผ์์ต์
ํธ๊ฐ์๋', '์ฃผ์์ต์
์ด๋ก ๊ฐ', '์
์ข
์ง์', '์
์ข
๋ฑ๋ฝ', '์์ฒด์
์ข
์ง์', '์์์
์ข
์ง์', '์ํฉ/๋ด์ค', 'ํ๋ฅ ', '์ฅ์์์๊ฐ', 'ํฌ์์ticker', '์ํํ๊ฐํญ๋ณ๊ฒฝ', 'VI๋ฐ๋/ํด์ ', 'ํฌ์์๋ณ๋งค๋งค', 'ํ๋ก๊ทธ๋จ๋งค๋งค', 'ํด์ธ์์ธ', '์ฃผ๋ฌธ์ฒด๊ฒฐ', 'ํ์์๊ณ ', 'ํ๋ฌผ์๊ณ ', '์์๊ธ', 'ํด์ธ์ฃผ๋ฌธ์ฒด๊ฒฐ', 'ํด์ธ์๊ณ ', '์๊ฐ์ฒด๊ฒฐ๋', '์ฃผ๋ฌธ์ฒด๊ฒฐ์๋ฒ์ํ', '์ฆ๊ฑฐ๊ธ', 'CFD์ฃผ๋ฌธ', 'CFD์ฒด๊ฒฐ', 'CFD๋ง์ง์ฝ๊ฒฝ๊ณ ', 'CFD์
์ถ๊ณ ', '์์ ํฌ๋ฉง', '์กฐ๊ฑด๊ฒ์', '์ผ๋ฐ์ ํธ', '๋ฆฌ์ผ์๊ณ ', 'ํด์ธ๋ฆฌ์ผ์๊ณ ', '๋ฆฌ์ผ์๊ณ ์ดํฉ', 'ํด์ธ์๊ณ ์ดํฉ', '์คํฑ๋ก์ค', '์ ๋ฌผ์ต์
ํฉ๊ณ', '์คํฑ์ฃผ๋ฌธ', 'CFD์ฃผ๋ฌธ์ฒด๊ฒฐ', 'CFD๋ฆฌ์ผ์๊ณ ', 'CFD๋ฆฌ์ผ์๊ณ ์ดํฉ', '๋ชจ๋ํฐ๋ง ์ค์๊ฐLOG', '์ฃผ์์ ๋ฌผํธ๊ฐ์๋', '์ค์๊ฐ์ฆ๊ฑฐ๊ธ', 'X-Ray์๊ฐ์ฒด๊ฒฐ๋', '๋งค์
์ธ๋์ฒด๊ฒฐ', '๋งค์
์ธ๋ํธ๊ฐ', '์ฝ๋ฅ์ค๊ฒฝ๋งค๋งค์ฒด๊ฒฐ', '๋ฐ์ดํฐ์
์ค์๊ฐ', 'ํ์ฝฉ์ฒด๊ฒฐ', 'ํ์ฝฉ์์ธ', 'ํ์ฝฉํธ๊ฐ์๋', 'ํ์ฝฉ๋จ์ผ๊ฐ์์ธ', 'ํ์ฝฉ์
์ข
์ง์', 'ํ์ฝฉ์ค์๊ฐ์ํํ๊ฐ', '์๋์๋์ฃผ๋ฌธ', '์๋์ฃผ๋ฌธ๊ฒฐ๊ณผ', 'TS๊ณ ์ ๋ณ๊ฒฝ', '์๊ณ ํธ์
', '๊ธฐ์ค๊ฐ๋ณ๊ฒฝ', '๋ฉํฐ์ฐจ๋จ', '์๊ณ ์ฒญ์ฐ์ญ์ ', 'ํ๊ฐํ์ฒด๊ฒฐ', 'ํ๊ฐํ์์ธ', 'ํ๊ฐํํธ๊ฐ์๋', 'ํ๊ฐํ๋จ์ผ๊ฐ์์ธ', 'ํ๊ฐํ์
์ข
์ง์', '๋ฏธ์ฒด๊ฒฐํต๋ณด์์คํ
', '์ฑ๋K์ค์๊ฐํฐ์ปค', '๋น
๋ฐ์ดํฐ์ข
๋ชฉ1๋ถ', '๋น
๋ฐ์ดํฐ์ข
๋ชฉ10๋ถ', '๋น
๋ฐ์ดํฐ์ข
๋ชฉ1์๊ฐ', '๋น
๋ฐ์ดํฐ์ข
๋ชฉ๋น์ผ', '๋น
๋ฐ์ดํฐ๋ด์ค', '์ ํธ๊ด๋ฆฌ์ํฌ์์ ๋ณด', '๋น
๋ฐ์ดํฐ๊ธ์์น', '๋น
๋ฐ์ดํฐ์ข
๋ชฉ30์ด', '์-์ข
๋ชฉํฌ์ฐฉ์ดํ', '์-๋งค๋๊ฐ์์์', '์-๋งค๋๊ฐ์ํฌ์ฐฉ', '์-์ฃผ๋ฌธ๊ฒฐ๊ณผ', '์-์ฒญ์ฐ์์', '์-์ฒญ์ฐ์๋ฃ', '์-๊ฐ์์์', '์-๋ด์กฐ๊ฑด์์์ ', '์-์ฃผ๋ฌธ์กฐ๊ฑด์์ ', '์-์ ํ์ฌ๋ถ', '์-๋ฉํฐ์ฐจ๋จ', '์-TS๋ณ๊ฒฝ', '์-๋ชจ์์ฃผ๋ฌธ์ฒด๊ฒฐ', '์-๋ชจ์ํ๋ฌผ์๊ณ ', '์-๋ชจ์ ์ฃผ๋ฌธ์ฒด๊ฒฐ', '์-๋ชจ์ ๋ฆฌ์ผ์๊ณ ', '์-๋ชจ์๋ฆฌ์ผ์๊ณ ์ดํฉ', '์ฑ๊ถ์ฒด๊ฒฐ', '์ฑ๊ถํธ๊ฐ์๋', '์์ก์ฑ๊ถ์ฒด๊ฒฐ', '์์ก์ฑ๊ถํธ๊ฐ์๋', 'CME์์ธ', 'CME๋ฏธ๊ฒฐ์ ์ฝ์ ', 'CMEํธ๊ฐ์๋', 'EUF์์ธ', 'EUFํธ๊ฐ์๋', 'EUREX์์ธ', 'EUREXํธ๊ฐ', '๋ฐฐ์น๋ฐ์ดํฐ๊ฐฑ์ ', '์ข
๋ชฉ๋ง์คํฐ๊ฐฑ์ ', 'ํด์ธ์ฃผ์์ฃผ๋ฌธ', 'ํด์ธ์ฃผ์์ฒด๊ฒฐ', 'ํด์ธ์ค์๊ฐ์๊ณ ์กฐํ', '๋ฏธ๊ตญ์
์ถ๊ณ ', '๋ฏธ๊ตญ์ข
๋ชฉ๋ณ๊ฒฝ', 'CME/EUREX์ฃผ๋ฌธ', 'CME/EUREX์ฒด๊ฒฐ', 'CME๋ฐฐ์น', 'EUREX๋ฐฐ์น', 'CME๋ฏธ์ฒด๊ฒฐ', 'CME์ค์๊ฐ์๊ณ ', 'CME์๊ณ ํฉ', '๋ถํ ๋ฐ๋ณต์ฃผ๋ฌธ']
>>> realtype_info = KiwoomOpenApiPlusRealType.get_realtype_info_by_name("์ฃผ์์์ธ")
>>> realtype_info
KiwoomOpenApiPlusRealType('0A', '์ฃผ์์์ธ', 21, [10, 11, 12, 27, 28, 13, 14, 16, 17, 18, 25, 26, 29, 30, 31, 32, 311, 822, 567, 568, 732])
>>> fid_list = KiwoomOpenApiPlusRealType.get_fids_by_realtype_name("์ฃผ์์์ธ")
>>> fid_list
[10, 11, 12, 27, 28, 13, 14, 16, 17, 18, 25, 26, 29, 30, 31, 32, 311, 822, 567, 568, 732]
>>> KiwoomOpenApiPlusRealType.Fid.get_name_by_fid(10)
'ํ์ฌ๊ฐ'
>>> fid_names = KiwoomOpenApiPlusRealType.get_field_names_by_realtype_name("์ฃผ์์์ธ")
>>> fid_names
['ํ์ฌ๊ฐ', '์ ์ผ๋๋น', '๋ฑ๋ฝ์จ', '๋งค๋ํธ๊ฐ', '๋งค์ํธ๊ฐ', '๋์ ๊ฑฐ๋๋', '๋์ ๊ฑฐ๋๋๊ธ', '์๊ฐ', '๊ณ ๊ฐ', '์ ๊ฐ', '์ ์ผ๋๋น๊ธฐํธ', '๊ฑฐ๋๋์ ์ผ๋๋น', '๊ฑฐ๋๋๊ธ์ฆ๊ฐ', '์ ์ผ๊ฑฐ๋๋๋๋น์จ', '๊ฑฐ๋ํ์ ์จ', '๊ฑฐ๋๋น์ฉ', '์๊ฐ์ด์ก', '822', '567', '568', '732']
์ค์๊ฐ ๋ฐ์ดํฐ ์์ฒญ (์ผ์ฑ์ ์ ์ฃผ์์์ธ ์ค์๊ฐ ๋ฐ์ดํฐ ์์ฒญ)
.. code-block:: python
>>> code_list = ["005930"]
>>> fid_list = KiwoomOpenApiPlusRealType.get_fids_by_realtype_name("์ฃผ์์์ธ")
>>> opt_type = "0" # ๊ธฐ์กด ํ๋ฉด์ ์ถ๊ฐ๊ฐ ์๋๋ผ ์ ๊ท ์์ฑ
>>> stream = entrypoint.GetRealDataForCodesAsStream(
... code_list,
... fid_list,
... opt_type,
... screen_no=None, # ํ๋ฉด๋ฒํธ, 0000 ์ ์ ์ธํ 4์๋ฆฌ ์ซ์ ์์๋ก ์ง์ , None ์ ๊ฒฝ์ฐ ๋ด๋ถ์ ์ผ๋ก ํ๋ฉด๋ฒํธ ์๋ํ ๋น
... infer_fids=True, # True ๋ก ์ค์ ์ ์ฃผ์ด์ง fid_list ๋ฅผ ๊ณ ์งํ์ง ๋ง๊ณ ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์์ ์ธ์๋ก ์ ๋ฌ๋ฐ๋ ์ค์๊ฐ๋ฐ์ดํฐ ์ด๋ฆ์ ๋ฐ๋ผ ์ ์ฐํ๊ฒ fid_list ๋ฅผ ์ถ๋ก
... readable_names=True, # True ๋ก ์ค์ ์ ๊ฐ fid ๋ง๋ค ์ซ์ ๋์ ์ฝ์ ์ ์๋ ์ด๋ฆ์ผ๋ก ๋ณํํ์ฌ ๋ฐํ
... fast_parse=False, # True ๋ก ์ค์ ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์๋ด์์ ๋ฐ์ดํฐ ๊ฐ ์ฝ๊ธฐ ์ GetCommRealData() ํจ์ ํธ์ถ ๋์ , ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์์ ์ธ์๋ก ๋์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ์ง์ ํ์ฉ, infer_fids ๊ฐ True ๋ก ์ค์ ๋ ๊ฒฝ์ฐ๋ง ์ ์๋ฏธํจ
... )
>>> for event in cancel_after(stream, 10):
... if event.name == "OnReceiveRealData":
... data = dict(zip(event.single_data.names, event.single_data.values))
... if "ํ์ฌ๊ฐ" in data:
... current_price = data["ํ์ฌ๊ฐ"]
์ฌ์ฉ ์ข
๋ฃ ํ ์ํธ๋ฆฌํฌ์ธํธ ๊ฐ์ฒด ๋ฆฌ์์ค ํด์ (์ฐ๊ฒฐ ํด์ ๋ฐ ์๋ฒ ์ดํ๋ฆฌ์ผ์ด์
์ข
๋ฃ)
์ง์ ``close()`` ๋ฉ์๋ ํธ์ถ:
.. code-block:: python
>>> entrypoint.close()
ํน์ ์ฒ์๋ถํฐ ์ปจํ
์คํธ ๋งค๋์ ํํ๋ก ๋ฆฌ์์ค ๊ด๋ฆฌ:
.. code-block:: python
>>> with KiwoomOpenApiPlusEntrypoint() as entrypoint:
... entrypoint.EnsureConnected()
... ...
Installation
------------
๋ํ์ ์ผ๋ก ์๋์ ๊ฐ์ด PyPI_ ๋ฅผ ํตํด์ ์ค์น๊ฐ ๊ฐ๋ฅํฉ๋๋ค:
.. code-block:: console
$ pip install koapy
๋ง์ฝ์ ๊ฐ๋ฐ ํ๊ฒฝ์ ๊ตฌ์ถํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ์๋ |poetry|_ ๋ฅผ ํ์ฉํด ๊ตฌ์ฑํฉ๋๋ค.
.. code-block:: console
$ # Install poetry (here using pipx)
$ python -m pip install pipx
$ python -m pipx ensurepath
$ pipx install poetry
$ # Clone repository
$ git clone https://github.com/elbakramer/koapy.git
$ cd koapy/
$ # Install dependencies and hooks
$ poetry install
$ poetry run pre-commit install
์ด์ธ์ ์์ธํ ์ค์น๋ฐฉ๋ฒ๊ณผ ๊ด๋ จํด์๋ Installation_ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
.. _PyPI: https://pypi.org/project/koapy/
.. |poetry| replace:: ``poetry``
.. _poetry: https://python-poetry.org/
.. _Installation: https://koapy.readthedocs.io/en/latest/installation.html
Usage
-----
์ค์น ์ดํ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ๋ฒ์ ๋ํด์๋ Usage_ ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
์ถ๊ฐ์ ์ผ๋ก ์ฌ์ฉ๋ฒ๊ณผ ๊ด๋ จ๋ ๋ค์ํ ์์๋ค์ examples_ ํด๋ ๋ฐ notebooks_ipynb_ ํด๋์์๋ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
ํน์๋ notebooks_ipynb_ ํด๋์ ``.ipynb`` ํ์ผ๋ค์ Github ์ ํตํด์ ๋ณด๋๋ฐ ๋ฌธ์ ๊ฐ ์๋ ๊ฒฝ์ฐ,
ํด๋น ๋
ธํธ๋ถ ์ฃผ์๋ฅผ nbviewer_ ์ ์
๋ ฅํ์ฌ ํ์ธํด ๋ณด์ธ์.
ํ์ฌ ์ํ ๋จ๊ณ์ด๊ธฐ ๋๋ฌธ์ ๋ง์ ๊ธฐ๋ฅ๋ค์ด ์ค์ ๋ก ๋ฌธ์ ์์ด ๋์ํ๋์ง ์ถฉ๋ถํ ํ
์คํธ๋์ง ์์์ต๋๋ค.
๋ง์ฝ์ ์ค์ ํธ๋ ์ด๋ฉ์ ์ฌ์ฉํ๋ ค๋ ๊ฒฝ์ฐ ์์ฒด์ ์ผ๋ก ์ถฉ๋ถํ ํ
์คํธ๋ฅผ ๊ฑฐ์น ํ ์ฌ์ฉํ์๊ธฐ ๋ฐ๋๋๋ค.
๊ฐ๋ฐ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์ผ๋ก ์ธํด ๋ฐ์ํ๋ ์์ค์ ๋ํด ์ด๋ ํ ์ฑ
์๋ ์ง์ง ์์ต๋๋ค.
๋ํ ์ํ ๋จ๊ณ์์ ๊ฐ๋ฐ์ด ์งํ๋๋ฉด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ตฌ์กฐ๊ฐ ๊ณ์ ๊ธ๊ฒฉํ๊ฒ ๋ณ๊ฒฝ๋ ์ ์์ผ๋ ์ฐธ๊ณ ๋ฐ๋๋๋ค.
.. _Usage: https://koapy.readthedocs.io/en/latest/usage.html
.. _examples: https://github.com/elbakramer/koapy/tree/master/koapy/examples
.. _notebooks_ipynb: https://github.com/elbakramer/koapy/tree/master/docs/source/notebooks_ipynb
.. _nbviewer: https://nbviewer.jupyter.org/
Features
--------
KOAPY ๋ ์๋์ ๊ฐ์ ๋ฐฉํฅ์ฑ์ ๊ฐ์ง๊ณ ๊ฐ๋ฐ๋์์ต๋๋ค.
GUI ์ดํ๋ฆฌ์ผ์ด์
๋ง์ผ๋ก ์ ํ๋์ง ์๋ ๋ค์ํ ์ฌ์ฉ์ฑ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
์ผ๋ฐ์ ์ผ๋ก ์ธํฐ๋ท ๋ฑ์ง์์ ์ ํ๊ธฐ ์ฌ์ด ๊ด๋ จ ์์๋ค์ ์ฒ์์ผ๋ก ๋ฐ๋ผ๊ฐ๋ค ๋ณด๋ฉด, ์๊ธฐ๋
๋ชจ๋ฅด๋ ์ฌ์ด์ Qt ๊ธฐ๋ฐ ์ดํ๋ฆฌ์ผ์ด์
์ ํ๋ ๋ง๋ค๊ณ , ๋ฒํผ์ ํ๋ ์ถ๊ฐํ๊ณ , ์ดํ ๋ชจ๋
๊ธฐ๋ฅ๋ค์ ์ฃ๋ค ํด๋น ๋ฒํผ์ ํด๋ฆญ ์ ์๋ํ๋ ์ฝ๋ฐฑ ํจ์ ํ๋์ ์ค์
๋ฃ๊ณ ์๋.. ์์ ์
๋ฐ๊ฒฌํ๊ฒ ๋๋๊ตฐ์.
.. code-block:: python
# https://wikidocs.net/4240
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QAxContainer import *
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PyStock")
self.setGeometry(300, 300, 300, 150)
self.kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
btn1 = QPushButton("Login", self)
btn1.move(20, 20)
btn1.clicked.connect(self.btn1_clicked)
btn2 = QPushButton("Check state", self)
btn2.move(20, 70)
btn2.clicked.connect(self.btn2_clicked)
def btn1_clicked(self):
ret = self.kiwoom.dynamicCall("CommConnect()")
def btn2_clicked(self):
if self.kiwoom.dynamicCall("GetConnectState()") == 0:
self.statusBar().showMessage("Not connected")
else:
self.statusBar().showMessage("Connected")
if __name__ == "__main__":
app = QApplication(sys.argv)
myWindow = MyWindow()
myWindow.show()
app.exec_()
์ฌ์ค ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ์ด์ OCX ํํ๋ก์ ์ ๊ณต๋๋ ๊ฒ๋ ์๊ณ , ๋ค๋ฅธ ์ฌ๋ฌ ๊ฐ์ง ์ด์ ๋ค๋ก
์ธํด ์ด์ฒ๋ผ GUI ์ดํ๋ฆฌ์ผ์ด์
ํํ๋ก ๊ฐ๋ฐ์ ํ๋ ๊ฒ์ด ์์ฐ์ค๋ฌ์ด ํ๋ฆ์ด๊ณ ์ ๋
์๋ชป๋์๋ค๊ณ ์๊ฐ๋์ง ์์ต๋๋ค.
๋ค๋ง ์ด๋ฌํ GUI ํ๊ฒฝ์ด ์ผ๋ฐ์ ์ผ๋ก Python ์์ REPL ํน์ Jupyter Notebook ๋ฑ์ ํตํด์
์ธํฐ๋ํฐ๋ธํ๊ฒ ๊ฒฐ๊ณผ ๊ฐ๋ค์ ํ์ธํด๊ฐ๋ฉด์ ์กฐ๊ธ์ฉ ๊ฐ๋ฐํด ๋๊ฐ๋ ๊ฒ๊ณผ๋ ๊ฑฐ๋ฆฌ๊ฐ ๋ฉ์ด์ง๋ฉด์
๊ฒฐ๋ก ์ ์ผ๋ก ์์ฐ์ฑ์ด ๋จ์ด์ง๊ฒ ๋๋ ๊ฑด ์๋๊ฐ ํ๋ ์๊ฐ์ด ๋ค์๊ณ , ๊ทธ๋ก ์ธํด KOAPY ์
๊ธฐ๋ณธ์ ์ธ ์ธํฐํ์ด์ค๋ ๊ธฐ๋ฐ์ด ๋๋ Qt ํ๊ฒฝ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ์ ์ผ๋ก ๊ณ ๋ คํ์ง ์์ผ๋ฉด์๋
๊ธฐ๋ฅ์ ์ฝ๊ฒ ์ฌ์ฉํด ๋ณผ ์ ์๊ฒ ๋ ํ๋ ๊ฒ ์ข๊ฒ ๋ค๊ณ ์๊ฐํ์ต๋๋ค.
๋ฐ๋ผ์ KOAPY ์ ๊ธฐ๋ณธ์ ์ธ ๋์์ธ์ ์ฐ์ ์ธ์ ์ผ๋ก ๋ดค์ ๋ ์ด๊ฒ ๋ด๋ถ์ ์ผ๋ก๋ Qt ๊ฐ์
ํ๊ฒฝํ์์ ๋์๊ฐ๋ค๋ ๊ฒ์ ๋ฐ๋ก ์์์ฑ ์ ์๊ฒ ๋ ๋์ด ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ธฐ๋ณธ์ ์ธ
๊ธฐ๋ฅ๋ค์ ์ด์ฉํ๋ ๋ฐ์ ์์ด์๋ ๋ฒํผ ํด๋ฆญ ๋ฑ์ ์ด๋ฒคํธ์ ๊ธฐ๋ฐํ ํธ์ถ๋ณด๋จ ์ผ๋ฐ์ ์ธ ํจ์
ํธ์ถ๊ณผ ๊ฐ์ ๋ช
๋ นํ ํ๋ก๊ทธ๋๋ฐ์ด ๊ฐ๋ฅํ๋๋ก ๋์์ธ ํ์์ผ๋ฉฐ, ๋ฌธ์์์๋ ํด๋น ์ฌ์ฉ
์๋๋ฆฌ์ค๋ค์ ์ค์ ์ ์ผ๋ก ๋จผ์ ์๊ฐํ๊ณ ์์ต๋๋ค.
.. code-block:: python
from koapy import KiwoomOpenApiPlusEntrypoint
with KiwoomOpenApiPlusEntrypoint() as entrypoint:
entrypoint.EnsureConnected()
is_connected = entrypoint.IsConnected()
print(is_connected)
๊ฒฐ๋ก ์ ์ผ๋ก PyQt5_ ํน์ PySide2_ ๋ฅผ ๊ธฐ๋ฐํ GUI ํ๊ฒฝ์ ์ฝ๋งค์ผ ํ์ ์์ด ์ผ๋ฐ์ ์ธ
๋ผ์ด๋ธ๋ฌ๋ฆฌ์ฒ๋ผ ๊ฐ์ ธ๋ค ํ์ฉํ ์ ์์ผ๋ฉฐ, ์ฌ์ ํ GUI ๊ธฐ๋ฐ ์ดํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์ด ํ์ํ๋ค๋ฉด
๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์์๋ค์ ํตํด ์ง์ GUI ๋ฐฉํฅ์ผ๋ก ๊ฐ๋ฐ์ ํ ์๋ ์์ต๋๋ค.
.. code-block:: python
from koapy.compat.pyside2.QtWidgets import QApplication, QMainWindow
from koapy import KiwoomOpenApiPlusQAxWidget
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.kiwoom = KiwoomOpenApiPlusQAxWidget()
...
ํจ์ ํธ์ถ๋ฑ์ ๊ณผ์ ์์ ์ผ๋ฐ์ ์ธ Python ์ ๊ด์ต์ ํฌ๊ฒ ํด์น์ง ์๋ ๋งค๋๋ฌ์ด ์ธํฐํ์ด์ค ๊ตฌ์ฑ
Qt ๋ฅผ ํตํด์ COM/OLE/OCX ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ค๋ฉด |dynamicCall|_ ํจ์๋ฅผ ํ์ฉํด์ผ ํฉ๋๋ค.
ํด๋น |dynamicCall|_ ํจ์๋ ํธ์ถํ๊ณ ์ ํ๋ ๋ฉ์๋์ ํ๋กํ ํ์
์ ๋ฌธ์์ด ํํ ์ธ์๋ก์
์
๋ ฅํ๋๋ก ๋์ด์๋๋ฐ, ๋จ์ํ๊ฒ๋ ๋งค๋ฒ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์ ํ ๋๋ง๋ค ํด๋น ๋ฉ์๋์
ํ๋กํ ํ์
์ด ์ด๋ป๊ฒ ์๊ฒผ๋์ง๋ฅผ ๋ฌธ์ ๋ฑ์ ์ฐธ๊ณ ํ๊ณ ์ผ์ผ์ด ์ ์ด ๋ฃ์ด์ค์ผ ํ ์ ์์ต๋๋ค.
.. code-block:: python
control = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
control.dynamicCall("SetInputValue(const QString&, const QString&)", "์ข
๋ชฉ์ฝ๋", code)
๊ทธ ๋ค์์ผ๋ก ๋ณด๋ค ๋์ ๋ฐฉ์์ผ๋ก๋ ์ด๋ฌํ ํ๋กํ ํ์
์ ๋ชจ๋ ํจ์๋ค์ ๋ํด์ ํ์ธํด์
๊ฐ๋จํ ๋ํผ ํจ์๋ค์ ๋ง๋ค์ด๋๊ณ ํ์ฉํ๋ ๊ฒ ์๊ฒ ์ฃ .
.. code-block:: python
def SetInputValue(name, value):
return control.dynamicCall("SetInputValue(const QString&, const QString&)", name, value)
์ผ๋ฐ์ ์ผ๋ก๋ ์ด๋ฐ ๋ฐฉ์๋ค๋ก๋ ์ถฉ๋ถํ ์ฌ์ฉํ๋๋ฐ ๋ฌด๋ฆฌ๊ฐ ์์ผ๋ฆฌ๋ผ ์๊ฐ๋ฉ๋๋ค๋ง
๊ฐ์ธ์ ์ผ๋ก๋ ์์ ๊ฐ์ ์ ๊ทผ ๋ฐฉ์์์ ๋ช ๊ฐ์ง ์ฐ๋ ค๋๋ ์ ๋ค์ด ์ ๊ฒฝ์ด ์ฐ์์ต๋๋ค.
- ํ๋กํ ํ์
์ ์
๋ ฅํ๊ฑฐ๋ ๋ํผ ํจ์๋ค์ ๋ง๋ค์ด ๋ฃ๋ ๊ณผ์ ์์ ๋ฐ์ํ ์ ์๋ ํด๋จผ์๋ฌ
- ๋ํผ ํจ์๋ค์ ๋ง๋ค์ด ๋์๋๋ผ๋ ์ถํ ํจ์์ ๋ชฉ๋ก ํน์ ํน์ ํจ์์ ํ๋กํ ํ์
์์
๋ณ๊ฒฝ์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ ๋งค๋ฒ ์ง์ ์ ์ธ ๋์์ด ํ์ํจ
๋ฐ๋ผ์ KOAPY ์์๋ ์ด๋ฌํ ์ง์ ํจ์ ๋ชฉ๋ก ๋ฐ ํจ์๋ณ ํ๋กํ ํ์
์ ๋ณด ํ์ธ, ๊ทธ๋ฆฌ๊ณ
์ด๊ฒ๋ค์ ํ์ฉํด ํน์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ณผ์ ๊น์ง๋ฅผ ์ด๋ป๊ฒ ํ๋ก๊ทธ๋๋ฐ์ ์ผ๋ก ๋์ํ ์
์์๊น๋ฅผ ๊ณ ๋ฏผํ๊ณ , ํ์ฌ๋ OpenAPI+ OCX ์ TypeLib ์ ๋ณด๋ฅผ ์ฝ์ด์ ๋ฉ์๋๋ค์ ๋์ ์ผ๋ก
์์ฑํ๋๋ก ๊ตฌํํด ์์์ ์ฐ๋ คํ๋ ์ ๋ค์ ํด๊ฒฐํ์ต๋๋ค.
์ต์ข
์ ์ผ๋ก ์ปจํธ๋กค ํจ์ ํธ์ถ ์ ๋งค๋ด์ผ์ ๋ช
์ธ์ ์ ํ์๋ ํํ ๊ทธ๋๋ก Python ํจ์์๋
๊ฒ์ฒ๋ผ ํธ์ถ์ด ๊ฐ๋ฅํ๋ฉฐ, ์ดํ๋ KOAPY ๊ฐ ์ ์ฐํ๊ฒ ์ฒ๋ฆฌํฉ๋๋ค. ๋งค๋ฒ ๋ช
์ธ์ ๋ง๊ฒ
|dynamicCall|_ ํจ์์ ์ธ์๋ฅผ ์ ์ด ๋ฃ๊ฑฐ๋, ๋ชจ๋ ์กด์ฌํ๋ ํจ์์ ๋ํด ๋ฏธ๋ฆฌ ๋ํผ ํจ์๋ฅผ
์ ์ํ๊ฒ ๋ง๋ค์ด๋์ ํ์๊ฐ ์์ต๋๋ค.
.. code-block:: python
control = KiwoomOpenApiPlusQAxWidget()
control.SetInputValue("์ข
๋ชฉ์ฝ๋", code)
์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ ๊ด๋ จ ๋ณต์กํ์ง ์์ ์ธํฐํ์ด์ค ์ ๊ณต
๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ผ๋ฐ์ ์ผ๋ก ์ด๋ ต์ต๋๋ค. ํนํ OpenAPI+ ๋ฐ KOAPY ์ ๊ฒฝ์ฐ๋ฅผ ์๋ก
๋ค์ด๋ณด๋ฉด ์๋์ ๊ฐ์ ๊ณ ๋ฏผํด ๋ณผ ๋งํ ๋ถ๋ถ๋ค์ด ์์ต๋๋ค.
* ์ฌ๋ฌ ์์ฒญ์ ๋ํ ์๋ต ๊ฒฐ๊ณผ๋ค์ ์ด๋ฒคํธ ํ์
๋ณ ํ๋์ฉ์ ํต๋ก๋ก ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๊ทธ๋ค
๊ฐ์ ๊ตํต์ ๋ฆฌ๋ถํฐ ํ์ํฉ๋๋ค.
* ์ฝ๋ฐฑ ํจ์ ๋ด์์ OpenAPI+ ๊ฐ ๊ธฐ๋ํ๋ ํน์ ๊ฐ์ด๋ํ๋ ๋ฐฉ์์ ๋ง์ถฐ์ ์ฒ๋ฆฌํด ์ฃผ์ด์ผ
ํ๋ ํน์ ๋จ๊ณ ํน์ ํ๋ก์ธ์ค๊ฐ ์กด์ฌํ๋ฉฐ ์ด๋ฅผ ์ ์ ํ ๋์ํด ์ฃผ์ด์ผ ํฉ๋๋ค.
* ์ฝ๋ฐฑ ํจ์์์ ๋ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ต์ข
์ ์ผ๋ก ํด๋น ๊ฒฐ๊ณผ๊ฐ ํ์ํ ๊ณณ์ผ๋ก (์ผ๋ฐ์ ์ผ๋ก ์์ฒญํ
๋์์๊ฒ๋ก) ์ ์ ๋ฌํด ์ฃผ์ด์ผ ํฉ๋๋ค.
KOAPY ์์๋ ์์ ๋ฌธ์ ๋ค์ ๋๋ฆ์ ๋ฐฉ์๋๋ก ๊ณ ๋ฏผํ์๊ณ ๋ฌธ์ ๋ค์ ํด๊ฒฐํ์ฌ ๋ ์ฝ๊ณ ๋์
์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
๊ตฌ์ฒด์ ์ผ๋ก ์๋ฅผ ํ๋ ๋ค์๋ฉด, ์ผ๋ฐ์ ์ธ ์์ฒญ-์๋ต ๊ณผ์ ์์ ๋ด๋ถ์ ์ผ๋ก gRPC
ํด๋ผ์ด์ธํธ-์๋ฒ ๊ด๊ณ๋ฅผ ๋ง๋ค์ด ์์ฒญ์๊ฐ gRPC ๋ฅผ ํตํด ํน์ ์์ฒญ์ ์ ๋ฌํ๋ฉด, ์ดํ gRPC
์๋ฒ์์๋ ํด๋น ์์ฒญ์ ๋ํ ์๋ต๋ค๋ง ์ถ๋ ค์ ๊ฒฐ๊ณผ๋ฅผ ์์ฒญ์์ธก์ ์คํธ๋ฆผ ํํ๋ก ์ ๋ฌํ๋๋ก
๋์์ธ๋์ด ์๋๋ฐ์. ์ฌ๊ธฐ์ ๊ตํต์ ๋ฆฌ ๋ฐ ๊ฒฐ๊ณผ ์ ๋ฌ ๋ฌธ์ ๊ฐ ์์ฐ์ค๋ฝ๊ฒ ํด๊ฒฐ๋๋ฉฐ ๋ถ์์ ์ผ๋ก
์ฌ์ฉ์ ์
์ฅ์์๋ ์ง์ ์ฝ๋ฐฑ ํจ์๋ฅผ ๋ค๋ฃจ๊ธฐ๋ณด๋ค๋ ๊ฐ์ ์ ์ผ๋ก ์คํธ๋ฆผ์
(``for`` ๋ฌธ์ ํ์ฉํ์ฌ) ๋ค๋ฃธ์ผ๋ก์จ ๋ ์ฝ๊ฒ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ด ํ์ํ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ
์ ์๋ ์ด์ ์ด ์์ต๋๋ค.
PySide2 ๋ฅผ ํตํ ์ง์ ์ ์ธ ๋ฐฉ์:
.. code-block:: python
# ์ปจํธ๋กค ๊ฐ์ฒด
control = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
# ์ด๋ฒคํธ ์๊ทธ๋
on_receive_tr_data_signal = SIGNAL("OnReceiveTrData(const QString&, const QString&, const QString&, const QString&, const QString&, int, const QString&, const QString&, const QString&)")
# ์ด๋ฒคํธ ์ฝ๋ฐฑ ํจ์
def on_receive_tr_data(screen_no, request_name, tr_code, record_name, prev_next, data_length, error_code, message, splmmsg):
handle_event(...)
# ์ด๋ฒคํธ ์ฒ๋ฆฌ ํ ํ์ํ๋ค๋ฉด ๋ฑ๋ก๋ ์ฝ๋ฐฑ ํจ์ ์ ๊ฑฐ
if should_disconnect:
control.disconnect(on_receive_tr_data_signal, on_receive_tr_data)
# ์ด๋ฒคํธ ์๊ทธ๋์ ์ฝ๋ฐฑ ํจ์ ์ฐ๊ฒฐ
control.connect(on_receive_tr_data_signal, on_receive_tr_data)
# TR ์
๋ ฅ๊ฐ ์ค์
control.dynamicCall("SetInputValue(const QString&, const QString&)", ..., ...)
...
# TR ์์ฒญ
err_code = control.dynamicCall(
"CommRqData(const QString&, const QString&, int, const QString&)",
request_name,
tr_code,
prev_next,
screen_no,
)
KOAPY ๋ฐฉ์:
.. code-block:: python
# TR ์์ฒญ ํ ์ด๋ฒคํธ๊ฐ ์คํธ๋ฆผ ํํ๋ก ๋ฐํ
for event in entrypoint.TransactionCall(request_name, tr_code, screen_no, inputs):
handle_event(event)
๊ฒฐ๋ก ์ ์ผ๋ก๋ ์ฌ์ฉ์๊ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ต์ํ์ง ์๋๋ผ๋ ๊ทธ๋ณด๋ค
๋น๊ต์ ์ฌ์ด ์ธํฐํ์ด์ค๋ฅผ ํตํด ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ ํ์ฉํ ์ ์์ต๋๋ค.
๋ ๋์๊ฐ์๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ถํฐ TR/์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์ฃผ๋ฌธ์ฒ๋ฆฌ๊น์ง
๋ค์ํ ์๋๋ฆฌ์ค์ ๋ํ ๊ธฐ๋ณธ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง์ ๊ตฌํํด ์ ๊ณตํด ์ฌ์ฉ์๋ค์ด ๋น๋๊ธฐ
ํ๋ก๊ทธ๋๋ฐ์ด ํ์ํ ๊ธฐ๋ฅ ์ค ์ฃผ๋ก ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ๋ค์ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋๋ก ํ์ต๋๋ค.
.. code-block:: python
stream = entrypoint.LoginCall()
stream = entrypoint.TransactionCall(request_name, tr_code, screen_no, inputs)
stream = entrypoint.RealCall(screen_no, codes, fids, opt_type)
stream = entrypoint.OrderCall(request_name, screen_no, account_no, order_type, code, quantity, price, quote_type, original_order_no)
์ฌ๋ฌ ์ผ๋ฐ์ ์ธ ์๋๋ฆฌ์ค๋ค์ ๋ํ ๋ค์ํ ๊ธฐ๋ณธ ๊ตฌํ์ฒด๋ค์ ์ ๊ณต
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
KOAPY ์์๋ ์ผ๋ฐ์ ์ผ๋ก ์์ฃผ ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ๋ค์ ๋ํด์ ์ฌ์ฉ์๋ค์ด ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋๋ก,
์๋ฅผ ๋ค์ด ์ฃผ์ ๊ธฐ๋ณธ์ ๋ณด ์์ฒญ๋ถํฐ ์ผ๋ด/๋ถ๋ด ๋ฑ ์์ธ ๋ฐ์ดํฐ ํ์ธ ๊ทธ๋ฆฌ๊ณ ์์๊ธ/์๊ณ ํ์ธ ๋ฑ๋ฑ์ ๋ํด ๋ฏธ๋ฆฌ ๊ตฌํ๋ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
.. code-block:: python
stock_info = entrypoint.GetStockBasicInfoAsDict(stock_code)
stock_chart_data = entrypoint.GetDailyStockDataAsDataFrame(stock_code)
account_deposit = entrypoint.GetDepositInfo(account_no)
account_evaluation = (
account_evaluation_summary,
account_evaluation_per_stock,
) = entrypoint.GetAccountEvaluationStatusAsSeriesAndDataFrame(account_no)
์ด ์ค์ ํจ์ ํธ์ถ ๊ฒฐ๊ณผ ์ค ํ
์ด๋ธ์ฑ ์ ๋ณด๋ค์ |pandas.DataFrame|_ ํ์
์ผ๋ก ์ ๊ณตํด ์ดํ ๋ถ์ ๋ฐ ์ฒ๋ฆฌ๊ฐ ์ ์ฉํ๊ฒ๋ ํ์ต๋๋ค.
.. |pandas.DataFrame| replace:: ``pandas.DataFrame``
.. _`pandas.DataFrame`: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html
๊ฐ๋ฐ๊ณผ์ ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ์ ๋๋๋ค ํ์๊ฐ ์๋๋ก ์์ฃผ ํ์ธ์ด ํ์ํ ์ฌ๋ฌ๊ฐ์ง ๋ฉํ ์ ๋ณด๋ค์ ํจ๊ป ์ ๊ณต
์ผ๋ฐ์ ์ผ๋ก ์ธํฐ๋ท ๋ฑ์ง์์ ์ ํ๊ธฐ ์ฌ์ด ๊ด๋ จ ๋๋ถ๋ถ ์์๋ค์ ์ฌ์ฉ์๊ฐ OpenAPI+ ์ ๋ํ
์ฌ๋ฌ ๊ฐ์ง ๋ฉํ์ ๋ณด๋ค์ ์ด๋ฏธ ๋ค ์๊ณ ์๋ค๋ ๊ฑธ ๊ฐ์ ํ๊ณ , ์๋๋ฉด ์ ์ด๋ ์ธ๋ถ ์ฐธ๊ณ ์๋ฃ๋ฅผ
ํ์ธํ๋ ๊ฒ์ ๊ฐ์ ํ๊ณ ์์ฑ๋์ด ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
์๋ฅผ ๋ค์ด TR ์ ์
๋ ฅ๊ณผ ์ถ๋ ฅ ๋ฐ์ดํฐ ๊ตฌ์กฐ, ์ค์๊ฐ ๋ฐ์ดํฐ๋ณ FID ๋ชฉ๋ก, ์๋ฌ์ฝ๋์ ๋ํ
์ค๋ช
๋ฌธ ๋ฑ์ด ์ด๋ฌํ ๋ฉํ์ ๋ณด๋ค์ ํด๋น๋๋๋ฐ์. ์์ ํจ์ ํธ์ถ ๋ฐฉ์์์์ ์ด์์ ๋น์ทํ๊ฒ,
์ด๋ฌํ ์ ๋ณด๋ค์ ๋งค๋ฒ ์ฐธ๊ณ ์๋ฃ๋ฅผ ํ์ธํ๊ณ ๊ทธ์ ๋ง์ถฐ ๊ฐ๋ฐํ๋ ๋ฐฉ์์๋ ์ด๋ ์ ๋ ํ๊ณ๊ฐ
์์ต๋๋ค.
๋ฐ๋ผ์ KOAPY ์์๋ ์ด๋ฌํ ์ ๋ณด๋ค์ ๊ฐ๋ฐํ๋ ๊ณผ์ ์์ ์ธ์ด ๋ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๋ฐ๋ก ์กฐํ
๋ฐ ํ์ฉ์ด ๊ฐ๋ฅํ๋๋ก ํฌํจ์์ผ ์ ๊ณตํฉ๋๋ค. ์ด๋ก ์ธํด ์ผ์ฐจ์ ์ผ๋ก ์ฌ์ฉ์ ์
์ฅ์์๋ ๋งค๋ฒ
๋งค๋ด์ผ_ ์ด๋ KOAStudio_ ๋ฅผ ์ด์ด์ ์ฐธ๊ณ ํ๊ณ ์ดํ ์ผ์ผ์ด ํ๋์ฉ ํ๋์ฝ๋ฉํ ํ์๊ฐ
์์ด์ก์ต๋๋ค.
.. code-block:: python
from koapy import KiwoomOpenApiPlusTrInfo
tr_info = KiwoomOpenApiPlusTrInfo.get_trinfo_by_code("opt10001")
from koapy import KiwoomOpenApiPlusRealType
realtype_info = KiwoomOpenApiPlusRealType.get_realtype_info_by_name("์ฃผ์์์ธ")
from koapy import KiwoomOpenApiPlusError
error_message = KiwoomOpenApiPlusError.get_error_message_by_code(-101)
๋ง๋ถ์ฌ์ TR ๊ด๋ จ ์ ๋ณด๋ ์ค์๊ฐ ๋ฐ์ดํฐ ๊ด๋ จ ์ ๋ณด๋ค์ OpenAPI+ ๊ฐ ์ค์น๋์ด ์๋ ๊ฒฝ์ฐ
ํด๋น ๊ฒฝ๋ก์ ๋ฐ์ดํฐ๋ฅผ ์ฐธ๊ณ ํด ๋์ ์ผ๋ก ์์ฑํ๋๋ก ๋์ด์๋๋ฐ์. ์ด๋ก ์ธํด ์ง์ ํ๋์ฝ๋ฉ
๋ฑ์ ํ์ ๋ ๋ฐ์ํ ์ ์๋ ์ด์ ์์ด, ๋งค๋ฒ ์
๋ฐ์ดํธ ์ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ด ๋ฐ๋ก๋ฐ๋ก ์ ์ฉ๋
์ ์๋ ์ด์ ๋ํ ๊ฐ์ง๊ณ ์์ต๋๋ค.
.. _๋งค๋ด์ผ: https://download.kiwoom.com/web/openapi/kiwoom_openapi_plus_devguide_ver_1.5.pdf
.. _KOAStudio: https://download.kiwoom.com/web/openapi/kiwoom_openapi_plus_devguide_ver_1.5.pdf#page=7
๋ผ์ด๋ธ๋ฌ๋ฆฌ ํธํ์ฑ์ผ๋ก ์ธํด ํน์ ํ๊ฒฝ์ด ๊ฐ์ ๋๋ ๊ฒ์ ์ฐํํ ์ ์๋๋ก ์์คํ
๊ตฌ์ฑ
ํค์์ฆ๊ถ์ OpenAPI+ ๋ 32bit ํ๊ฒฝ๋ง ์ ๊ณตํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ฌ์ฉํ๋ ์ชฝ๋ ์์ฐ์ค๋ฝ๊ฒ
32bit ๊ธฐ๋ฐ์ด ๋์ด์ผ ํฉ๋๋ค.
์ฌ๊ธฐ์๋ Python_ ์ 32bit ๋ก ์ฌ์ฉํด์ผ ํ๋ ์กฐ๊ฑด์ด ๋ถ๊ฒ ๋๋ ๊ฒ์ธ๋ฐ์. ๋ช๋ช ์ธ๋ถ
์๋ํํฐ๋ค์์๋ ๋ ์ด์ 32bit ๋ฅผ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ๋ ๋ง์ ๋ค์ํ ์๋ํํฐ ๊ธฐ๋ฅ๋ค๊ณผ
์ ๋ชฉ์ํค๊ธฐ์๋ 32bit ์ ์ฝ์ด ๋ฒ๊ฑฐ๋ก์ด ์ ์ด ๋ง์ต๋๋ค.
KOAPY ์์๋ ์ด๋ฅผ ํด์ํ๊ธฐ ์ํด gRPC_ ์๋ฒ-ํด๋ผ์ด์ธํธ ํํ์ ๊ตฌ์ฑ์ ์ก์ ์๋ฒ์์๋
32bit ๊ธฐ๋ฐ์ผ๋ก OpenAPI+ ์ ํต์ฌ ๊ธฐ๋ฅ๋ง ์ ๊ณตํ๋๋ก ํ๊ณ , ํด๋ผ์ด์ธํธ์์๋ ์ด๋ฐ 32bit
์ ์ฝ ์์ด ๊ฒฐ๊ณผ๋ค์ ๋ฐ์์ ์ดํ ์ฌ๋ฌ ์๋ํํฐ ๊ธฐ๋ฅ๋ค๊ณผ ํจ๊ป ํ์ฉํ ์ ์๋๋ก ํ์ต๋๋ค.
์์์๋ ์ฃผ๋ก 32bit ์ ์ฝ์ ๊ฐ์ง๊ณ ์ด์ผ๊ธฐํ์ง๋ง ๋ ๋์๊ฐ์๋ gRPC_ ์ ๋ง์ ์ธ์ด๋ค์
๋ํ ํ์ฅ์ฑ์ ํ์ฉํ์ฌ Python ์ด์ธ์ gRPC_ ์์ ์ง์ํ๋ ๋ค๋ฅธ ์ธ์ด๋ค๋ก ํด๋ผ์ด์ธํธ๋ฅผ
์์ฑํด ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก๋ ํ์ฅ์ด ๊ฐ๋ฅํฉ๋๋ค.
๋ํ Python_ ์์ Qt_ ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ PyQt5_ ํน์ PySide2_ ๋ฅผ ์ฌ์ฉํด์ผ ํ๋๋ฐ์.
KOAPY ์์๋ ๋ผ์ด์ ์ค ๋ฑ์ ๊ณ ๋ คํด์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก PySide2_ ๋ฅผ ์ฌ์ฉํ๋๋ก ๋์ด์์ง๋ง,
์ฌ์ฉ์์ ํ์์ฑ์ ๋ฐ๋ผ PySide2_ ๋์ PyQt5_ ๋ฅผ ์ฌ์ฉํ๋ ค ํ ๋ ์ฝ๊ฒ ๋ณ๊ฒฝํ ์ ์๋๋ก
|qtpy|_ ๋ฅผ ํตํด์ ์ง์ํ๊ณ ์์ต๋๋ค.
.. _gRPC: https://grpc.io/
.. _Python: https://www.python.org/
.. _Qt: https://www.qt.io/
.. _PyQt5: https://www.riverbankcomputing.com/software/pyqt/
.. _PySide2: https://doc.qt.io/qtforpython/index.html
.. |qtpy| replace:: ``qtpy``
.. _qtpy: https://github.com/spyder-ide/qtpy
ํค์์ฆ๊ถ์ OpenAPI+ ๊ธฐ๋ฅ ์์ฒด ์ด์ธ์ ๋ถ์์ ์ผ๋ก ํ์ํ ๋งํ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐ๋ก ์ ๊ณต
KOAPY ๋ ํค์์ฆ๊ถ์ OpenAPI+ ํต์ฌ ๊ธฐ๋ฅ ์ด์ธ์ ์ ์ฒด์ ์ธ ๊ฐ๋ฐ ๋ฐ ํ์ฉ์ ํ์ํ ๋ค์ํ
๋ถ๊ฐ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐ๋ก ์ ๊ณตํฉ๋๋ค.
- TR ํธ์ถ ์ ํธ์ถ ํ์ ์ ํ ํํผ
- KRX ๊ฑฐ๋์ ํด์ฅ์ผ ํ์ธ (|exchange_calendars|_ API ํ์ฉ)
- ์๋ ๋ฐ ๋ฉ์์ง ๊ธฐ๋ฅ (|discord.py|_ API ํ์ฉ)
.. |exchange_calendars| replace:: exchange_calendars
.. _exchange_calendars: https://github.com/gerrymanoim/exchange_calendars
.. |discord.py| replace:: discord.py
.. _discord.py
: https://discordpy.readthedocs.io/en/stable/
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฟ๋ง ์๋๋ผ ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐ์ ์ธ ์ฝ๋ ๊ตฌํ์์ด ์ฝ๊ฒ ์ฌ์ฉํด๋ณผ ์ ์๋๋ก (+ ๊ฐ๋
์ฆ๋ช
๋ชฉ์ ์ผ๋ก์) CLI ์ ๊ณต
๊ตณ์ด Python ์ฝ๋๋ฅผ ์์ฑํ์ง ์๋๋ผ๋ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ๋ค์ ํ์ฉํด ๋ณผ ์ ์๋๋ก ์ฌ๋ฌ
์ปค๋งจ๋๋ฅผ ํฌํจํ๋ CLI ๋ฅผ ์ ๊ณตํฉ๋๋ค.
CLI ๋ฅผ ํ์ฉํ๋ฉด ๋ง์ผ๋ณ ์ฝ๋ ๋ชฉ๋ก ํ์ธ, ์ฃผ์ ๊ธฐ๋ณธ ์ ๋ณด ํ์ธ, ์ผ๋ด/๋ถ๋ด ๋ฐ์ดํฐ ํ์ธ ๋ฐ
์ ์ฅ, ์ค์๊ฐ ๋ฐ์ดํฐ ๊ตฌ๋
๋ฑ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ ์ฝ๋ ๊ตฌํ ์์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
.. code-block:: console
$ koapy get stockcode --market=0
$ koapy get stockinfo --code=005930
$ koapy get daily --code=005930 --output=005930.xlsx
$ koapy watch --code=005930 --realtype="์ฃผ์์์ธ"
์๋ฒ๋ CLI ์ปค๋งจ๋๋ก ์ฝ๊ฒ ๋์ธ ์ ์์ต๋๋ค.
.. code-block:: console
$ koapy serve
Licensing
---------
KOAPY ๋ ๋ค์ค ๋ผ์ด์ ์ค ๋ฐฉ์์ผ๋ก ๋ฐฐํฌ๋๋ฉฐ,
์ฌ์ฉ์๋ ์์ ์ ์๋ ๋ฐ ์ฌ์ฉ ๋ฐฉ์์ ๋ฐ๋ผ ์๋ ๋ผ์ด์ ์ค ์ต์
๋ค ์ค ํ๋๋ฅผ ์ ํํด ์ฌ์ฉํ ์ ์์ต๋๋ค.
* `MIT License`_
* `Apache License 2.0`_
* `GNU General Public License v3.0`_ or later
๋ผ์ด์ ์ค ์ ํ๊ณผ ๊ด๋ จํ์ฌ ์ถ์ฒํ๋ ๊ฐ์ด๋๋ผ์ธ์ ์๋์ ๊ฐ์ต๋๋ค.
MIT License
~~~~~~~~~~~
* ์ผ๋ฐ์ ์ธ ์ฌ์ฉ์์๊ฒ ์๋ง์ต๋๋ค.
* ์งง๊ณ ๋จ์ํ ๋ผ์ด์ ์ค๋ฅผ ์ ํธํ์๋ฉด ํด๋น ๋ผ์ด์ ์ค๋ฅผ ์ ํํ์ธ์.
Apache License 2.0
~~~~~~~~~~~~~~~~~~
* MIT ๋ผ์ด์ ์ค์ ํฐ ์ฐจ์ด๋ ์์ง๋ง, ํนํ์ ๊ด๋ จํด์ ๋ช
์์ ์ธ ํ๊ฐ์กฐํญ์ด ์์ต๋๋ค.
* ์ถํ ํนํ๊ถ ์นจํด ์์ก์ด ์ฐ๋ ค๋๋ ๊ฒฝ์ฐ MIT ๋ผ์ด์ ์ค ๋์ ์ ์ ํํ์๋ฉด ๋ฉ๋๋ค.
GNU General Public License v3.0 or later
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* FSF_/GPL_ ์ด ์ถ๊ตฌํ๋ Copyleft_ ์ ๊ฐ์น๋ฅผ ๋ฐ๋ฅด์ ๋ค๋ฉด ์ ํ ๊ฐ๋ฅํ ์ต์
์ค ํ๋์
๋๋ค.
* ์ด์ธ์ backtrader_ ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ ํ์ฉํ์๋ ๊ฒฝ์ฐ, KOAPY ๋ **๋ฐ๋์** GPLv3+ ๋ก๋ง ๋ฐฐํฌ๋์ด์ผ ํฉ๋๋ค.
* ๊ตฌ์ฒด์ ์ผ๋ก |koapy.backtrader|_ ๋ชจ๋ ํ์์ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ๋ค๋ฉด GPLv3+ ๋ฐฐํฌ ์กฐ๊ฑด์ ํด๋น๋ฉ๋๋ค.
* ์ด๊ฒ์ backtrader_ ๊ฐ GPLv3+ ๋ก ๋ฐฐํฌ๋๊ณ ์์ผ๋ฉฐ,
ํด๋น ๋ผ์ด์ ์ค์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ๊ทธ๊ฒ์ ์ฌ์ฉํ๋ ์ํํธ์จ์ด ๋ํ GPLv3+ ๋ก ๋ฐฐํฌ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
๊ฐ ๋ผ์ด์ ์ค์ ํ๊ฐ ๋ฐ ์๊ตฌ์ฌํญ๊ณผ ๊ด๋ จํด์ ์ฝ๊ฒ ์ ๋ฆฌ๋ ๋ด์ฉ์ `tl;drLegal`_ ์์ ์ฐธ๊ณ ํ์ค ์ ์์ต๋๋ค.
๋ค๋ง ์์ ๋ด์ฉ์ด ๋ฒ๋ฅ ์ ์กฐ์ธ์ ์๋ ์ ์ฐธ๊ณ ๋ฐ๋๋๋ค.
.. _`MIT License`: https://spdx.org/licenses/MIT.html
.. _`Apache License 2.0`: http://www.apache.org/licenses/LICENSE-2.0
.. _`GNU General Public License v3.0`: https://www.gnu.org/licenses/gpl-3.0.html
.. _FSF: https://www.fsf.org/
.. _GPL: https://www.gnu.org/licenses/licenses.html#GPL
.. _Copyleft: https://www.gnu.org/licenses/copyleft.html
.. _backtrader: https://github.com/mementum/backtrader
.. |koapy.backtrader| replace:: ``koapy.backtrader``
.. _`koapy.backtrader`: https://github.com/elbakramer/koapy/tree/master/koapy/backtrader
.. _`tl;drLegal`: https://tldrlegal.com/
Credits
-------
์ด ํจํค์ง๋ Cookiecutter_ ์ `elbakramer/cookiecutter-poetry`_ ํ๋ก์ ํธ ํฌํ๋ฆฟ์ ์ฌ์ฉํ์ฌ ์์ฑ๋์์ต๋๋ค.
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`elbakramer/cookiecutter-poetry`: https://github.com/elbakramer/cookiecutter-poetry