From a190a40bdedafcc463dcea95fc5a847338a6abb3 Mon Sep 17 00:00:00 2001 From: gerd Date: Wed, 9 Sep 2026 00:47:34 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=BC=EB=8B=A8=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 13 +++- app/__pycache__/__init__.cpython-313.pyc | Bin 148 -> 147 bytes app/__pycache__/main.cpython-313.pyc | Bin 4992 -> 5408 bytes app/core/__pycache__/__init__.cpython-313.pyc | Bin 153 -> 152 bytes app/core/__pycache__/auth.cpython-313.pyc | Bin 8183 -> 11994 bytes app/core/__pycache__/config.cpython-313.pyc | Bin 6516 -> 8520 bytes app/core/__pycache__/database.cpython-313.pyc | Bin 2160 -> 2159 bytes app/core/__pycache__/logger.cpython-313.pyc | Bin 1572 -> 1571 bytes .../__pycache__/rate_limiter.cpython-313.pyc | Bin 2152 -> 2151 bytes app/core/config.py | 45 ++++++++++++ .../__pycache__/__init__.cpython-313.pyc | Bin 155 -> 154 bytes .../__pycache__/collector.cpython-313.pyc | Bin 4835 -> 4834 bytes .../__pycache__/scheduler.cpython-313.pyc | Bin 3663 -> 3662 bytes .../strategy_engine.cpython-313.pyc | Bin 7971 -> 9046 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 166 -> 165 bytes .../__pycache__/base.cpython-313.pyc | Bin 2391 -> 2390 bytes .../__pycache__/conditional.cpython-313.pyc | Bin 2632 -> 2631 bytes .../__pycache__/periodic.cpython-313.pyc | Bin 3347 -> 3346 bytes .../__pycache__/technical.cpython-313.pyc | Bin 7703 -> 7702 bytes app/engine/strategy_engine.py | 37 ++++++---- .../__pycache__/__init__.cpython-313.pyc | Bin 155 -> 154 bytes app/models/__pycache__/stock.cpython-313.pyc | Bin 5198 -> 5197 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 156 -> 155 bytes .../__pycache__/dashboard.cpython-313.pyc | Bin 4320 -> 5002 bytes .../__pycache__/stocks.cpython-313.pyc | Bin 4948 -> 6049 bytes .../__pycache__/strategies.cpython-313.pyc | Bin 5430 -> 5429 bytes .../__pycache__/trading.cpython-313.pyc | Bin 4760 -> 5746 bytes .../__pycache__/websocket.cpython-313.pyc | Bin 3899 -> 3898 bytes app/routers/trading.py | 2 +- .../__pycache__/__init__.cpython-313.pyc | Bin 157 -> 156 bytes .../__pycache__/account.cpython-313.pyc | Bin 6652 -> 6651 bytes .../__pycache__/market_data.cpython-313.pyc | Bin 8272 -> 8700 bytes .../__pycache__/realtime.cpython-313.pyc | Bin 11014 -> 11472 bytes .../__pycache__/trading.cpython-313.pyc | Bin 8542 -> 12972 bytes app/services/market_data.py | 12 +++- app/services/trading.py | 64 +++++++++++++----- config.example.yaml | 43 ++++++++++++ data/stock.db-shm | Bin 32768 -> 0 bytes data/stock.db-wal | 0 requirements.txt | 1 - 40 files changed, 181 insertions(+), 36 deletions(-) create mode 100644 config.example.yaml delete mode 100644 data/stock.db-shm delete mode 100644 data/stock.db-wal diff --git a/.gitignore b/.gitignore index 3bb885c..411d601 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,14 @@ -.venv +app/__pycache__/ +app/core/__pycache__/ +app/engine/__pycache__/ +app/engine/strategies/__pycache__/ +app/models/__pycache__/ +app/routers/__pycache__/ +app/services/__pycache__/ __pycache__/ -*.pyc +.venv/ .auth_cache.json -config.yaml .env +*.pyc output.log +config.yaml diff --git a/app/__pycache__/__init__.cpython-313.pyc b/app/__pycache__/__init__.cpython-313.pyc index d9cf1ebbd7feac7d7c03238f4d01d29bbb7ac515..3243ec818b2bc9269da87cf545a66709babf29bc 100644 GIT binary patch delta 54 zcmbQjIGK_AGcPX}0}!OWkDSPDEN|pu6%(FclwDkqn4B6DP+5|ZpBGbHlAoNNSXz>w IJ2B1-0K3c*OaK4? delta 55 zcmbQtIE9h>GcPX}0}yn+iI~W3tYGY76%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn3;@ve5{&== diff --git a/app/__pycache__/main.cpython-313.pyc b/app/__pycache__/main.cpython-313.pyc index 04e3c002edc6daeb1488c22e23d84ce88c360639..2839c7e32d3676e6b609e4c131fd6a49118a422f 100644 GIT binary patch delta 1024 zcmZ`&TSydP6#mc7%sS4_?&?_UTB}ZLtL>&MR9jG+7ZTh>SWF2ijaYS7t#wCcccTkY zL@$-l!yiOF1r_vI`4aTvrQG!;#0trRUh1U_S@w_x(fP-s8~w~V^PTVe&N%}!bE@WQ zz_DbvO9W5i{oaEf`L<)3w_N4v8b|e5y>K3U4TN-;GM$~!%8jU`f^@ro6r^Jf-Ca;m zs~qKgXF&+)jMWc?bjiATqZEP_lS;bFf7*Wy`E5i68(~v_dOEyjz5cJd`p=pMMG2TQ zC=L*jv_h<(>S9}IYbQxTGbA8Ec5;;@0j5@p+U8y29tc@h#;Bx{tT?f==--~$Gm*r` zF>YX-!_c>J3Gmy-UWwJvxE6=BMLX3lt+!L}JEUdQx0i0czH>``d-=?pYopo9|LW(j zzZ<&JuJk6!ht-l-8FqoODsz))H*l-qWQUhtebQKCP$yXl77P^`tYn zD4xye!f!IXigacoK}XBzDSd{(YnWC*`kFnY=-BGcU}q1 zDS<~y?Yvw&E7v}kl?!cW+h*ONC-T-$A}MszZ?@yGgC3J>PJWb$z3%f%r+bM2>qc+v zY;1OI_-=e&Zkm&uzAkc1`0_y^!iJ~3? z=rzZ7cD6o6;gU08k=3R_yL)UH^!EGTQKNT4l-*sv#!X%E(bF$fE2fz@R(8V_x} z8BILQ&6Clai3dZxcrdo*=j74E_H5!+(i5JXSz?n;_Pu=XdoOQh-$r&8+(#Ve0Qh_z zZ9e$z_~ahZmY)>*;t@MC^AdWT0OY4hpPU3hvQ0u4Y)(|NNl`Wd$6f&WnK6->V|VJm zTMwe4O?I9+t_8CWov2fAY+Wa841H&9a1kZ#ONg}xqLUk-3A3;QD`1tH0~N@E8y|4I z#$n*NjU$t!Dp0a_KH5cyrk#%uTDbYtsA{JnwBveZ{lU)sV%;dkJ;TrW%q?sW1Aqx1(B}{6v*tH9 zYQ4wf&$H*02;3C|xj5L(z?@9)UX5Yiy9lx0i}^9{Q`tB!x(TQVboWXSbI%8r5Z&WV zmZ36F_bw6cyPzV{ea>VFDq*@GVTe!55O3_b+@2|nx3vVj9+}n+;5iEC;DZ~*U`0!Z$8snYihA>)bwSn*OA|QhZ8aB z3{H|oU+Qdo-@qxN4()ZVdbh4VR_l6@j7Ui4gAw IJ2B200KSb9U;qFB delta 55 zcmbQiIFph4GcPX}0}yn*iI~W3tYG0{6%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn8UWMp5~2VA diff --git a/app/core/__pycache__/auth.cpython-313.pyc b/app/core/__pycache__/auth.cpython-313.pyc index 642a478a687bd2f2f03d3bd335963e364c2c49e4..4e05382869f4c0271a2f539ecc7375766c71e5ad 100644 GIT binary patch literal 11994 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 W!6L5p0b%hT3juZG`cet&(SHHHC#cu} delta 2978 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 G{rv~1S!o*p diff --git a/app/core/__pycache__/database.cpython-313.pyc b/app/core/__pycache__/database.cpython-313.pyc index 08551a6b1c47adfde8a13635edd96b9e06f48ffb..5aaa5f1edf4e188a7a57b9e4099a77591abe50c0 100644 GIT binary patch delta 59 zcmew$@LquTGcPX}0}!OWkIZ_vk(ZZA-rU71COp3=ySN}RIW;DrvLquvFQ&L8KRG+G Nv?M=wvlWvW8vs{q6omi) delta 60 zcmaDa@Iiq0GcPX}0}!lx6OnazBQGzLf`yA!On81#c5y*sa%xOKWl2VUUQBRFesZ>B OX-R%=;$}-GGd2KkITXME diff --git a/app/core/__pycache__/logger.cpython-313.pyc b/app/core/__pycache__/logger.cpython-313.pyc index d071661811aad8f19bd72685a00af5c0cda974dd..f237dadbed3c8de736b6066d9e1d2db7abcd3fbc 100644 GIT binary patch delta 59 zcmZ3&vzUkXGcPX}0}!OWkIb65k=KSr-qgh^COp3=ySN}RIW;DrvLquvFQ&L8KRG+G Nv?M=wb0*7fMgT146leed delta 60 zcmZ3?vxJBDGcPX}0}w2J6OmQ6k=KSr!OX=fCOp3=ySN}RIW;DrvLquvFDAGoKRMg6 Ov?M<_adQUCZbkq^kQAr@ diff --git a/app/core/__pycache__/rate_limiter.cpython-313.pyc b/app/core/__pycache__/rate_limiter.cpython-313.pyc index b036b5770e824b42eee6ccad34140223ba83602b..e802330aecff0fdb1a5bea71ed88b3cd8ceae8f4 100644 GIT binary patch delta 59 zcmaDM@LYiRGcPX}0}!OWkIb^&$a|Dg-rB_~COp3=ySN}RIW;DrvLquvFQ&L8KRG+G Nv?M=w^C!kKHUL(U6?Oms delta 60 zcmaDZ@Irw1GcPX}0}w2G6OpB|k@qO0f{lw+On81#c5y*sa%xOKWl2VUUQBRFesZ>B OX-R%=;^vQxWo!U%<`ugD diff --git a/app/core/config.py b/app/core/config.py index 5e0ef38..88bb62a 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,11 +1,34 @@ from __future__ import annotations +import os from pathlib import Path from typing import Any import yaml +def _load_dotenv(path: Path) -> None: + """간단한 .env 파서 (python-dotenv 의존성 대신 내장 구현).""" + if not path.exists(): + return + for line in path.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, _, value = line.partition("=") + key = key.strip() + value = value.strip().strip('"').strip("'") + if key and key not in os.environ: + os.environ[key] = value + + +_load_dotenv(Path(".env")) + + +def _env(name: str, default: Any = None) -> Any: + return os.environ.get(name, default) + + class KISConfig: app_key: str = "" app_secret: str = "" @@ -14,6 +37,27 @@ class KISConfig: hts_id: str = "" server_mode: str = "vps" + def apply_env(self) -> None: + app_key = _env("KIS_APP_KEY") + app_secret = _env("KIS_APP_SECRET") + account_no = _env("KIS_ACCOUNT_NO") + account_code = _env("KIS_ACCOUNT_CODE") + hts_id = _env("KIS_HTS_ID") + server_mode = _env("KIS_SERVER_MODE") + + if app_key: + self.app_key = app_key + if app_secret: + self.app_secret = app_secret + if account_no: + self.account_no = account_no + if account_code: + self.account_code = account_code + if hts_id: + self.hts_id = hts_id + if server_mode: + self.server_mode = server_mode + class AppConfig: name: str = "StockAutomation" @@ -58,6 +102,7 @@ class Settings: self.trading = TradingConfig() self.rate_limit = RateLimitConfig() self._load_yaml() + self.kis.apply_env() def _load_yaml(self) -> None: yaml_path = Path("config.yaml") diff --git a/app/engine/__pycache__/__init__.cpython-313.pyc b/app/engine/__pycache__/__init__.cpython-313.pyc index 809df2308156cd968f03e862a776e7c147fddc36..29b131a112ede05bac9efecea6d4c107e8d6c741 100644 GIT binary patch delta 54 zcmbQuIE#_{GcPX}0}!OWkDSPDEN|&z6%(FclwDkqn4B6DP+5|ZpBGbHlAoNNSXz>w IJ2B1{0KcaaXaE2J delta 55 zcmbQmIGd6CGcPX}0}yn-iI~W3tYGD06%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn768>b5~~0J diff --git a/app/engine/__pycache__/collector.cpython-313.pyc b/app/engine/__pycache__/collector.cpython-313.pyc index 7e5f3074ed4cee8180f1ee1cdd10b7b314111ea1..13a02d7b99894f64ac43bf2dc611d5eb76250812 100644 GIT binary patch delta 59 zcmaE?`bd@cGcPX}0}!OWkIXu-k@p>=yp@YpOn81#c5y*sa%xOKWl2VUUQBUGesXqV NX-R(WW@)Aq0swZE6~_Po delta 60 zcmaE)`dF3sGcPX}0}#}`jmS#g$oq~_!P>}3p9~Geh diff --git a/app/engine/__pycache__/scheduler.cpython-313.pyc b/app/engine/__pycache__/scheduler.cpython-313.pyc index 1130af11b24880db2400f0dd3f6357f2ac359ffa..45914de94df68b68a90c01f0930327b8896fbbde 100644 GIT binary patch delta 43 xcmX>vb54f$GcPX}0}!OWkIdS#k@pi5qtzs45w7Br{N(J!(vtk#&GO6!+yF7y4Zr{Z delta 44 ycmX>nb6$q`GcPX}0}!;njmS#b$oq+j(RvcI2zPKvesZ>BX-R%=;$}H!18x8~h7GO& diff --git a/app/engine/__pycache__/strategy_engine.cpython-313.pyc b/app/engine/__pycache__/strategy_engine.cpython-313.pyc index fb28290c70058a2d3c8811dc595f43b975573691..57ba9d2f421b2900237f3acc6407b98f28405443 100644 GIT binary patch delta 3627 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 delta 2785 zcmZ8jTWnj$5#8l|@F~7YQM5!-FO#$^Id-f_kt0`%ZAqnKRUpMisN}LY-9UTq=;rA&`@+*$H@aa+Iu#>y+7YzMK_h{s+sT7=Sm6;+B57 zQD4pJjpCK*O}$Z5_$A>2J#zOQ;VB&*fQ5#oQc8=g2StyjGeuX+TLq>T8?0mq#j3^z z!K8@*2^cKK|0+F4W85x(N9+S7|Fttr-{kK*i(-yW%<(SQu+I`8vvlrRLh53SsT4!w$2_&%pZKmNk{uU2@;1~t~;h35UEZM zTB_8`g{%6OVN;y(UYVN6QY^e1T!xI`}rM}iMWTq+_xZ5whT2wd8m6FO>gQcCVOtq9= zF&qckT2VE_s!5e{U8(Zlc;`X}RW&CB!+bp$khWIeh&%*ZP2p#Jy^(uM%@beU4Q2L1 z3(e5NZfJ4G@j;?*$I5T{mi@84=!s_Z#Cw*#)AP;K^SjZP_{Y8y_3(Dz!kRQ0{j3__oaH1SR{e$Sa1Wf)k;eg3Qk%IhCcFasl552uJ zqJ!bVc9P*ir6^q|EeY5*jo7E0OSVq?{os4EZvg8&zl;1*m)=+l+e_ zZBqzK{Pjp=8&iNyAYhd;JA!$Mf>J6OahlN}0MZWg&e$9(W&jKs+Du8+ z*aA*m=629=w9Tq+9>j2%c~vMWx?)JCFZ&v}Y7P%z`%2ATlI&#(GI~2SYrg#k-bxLo>-i|rN}=8B78(=fwK7Fr~{08=BLJ9t|z)XSB+I`ZXa@qJu) z4&Wx)w;lh$AN*PBcIxf1U4Q?c|5($1?A@`wp~>dZI+e{FhYd_wu?%!py)cQyt1jQTA}dU1A@$i4x=*y za;hZ(-Xc#dR2q09J0-`HXhQ0Ia-OE7@c)*m9~3D;8vfJ)_R}qvacMecI0}WeO`usV z6d2Y8gC~s`ZtZGSsTMD*wXKl`t1*#p_l8u=Cc}dX3jdR+Bh^N^4DCtARj(nKb!}b{ zwj(2go|?+8!PBIT)D(6FUL|ZLt@kVV1v54mc&(U||E@P7W6`h<-t0|MH;?rV%Ghs= z7=N!fyuG2aN(0ztUUV`WC!XHqg85MT0fL!I$dqAw0qSY9s-9(U!xR%w`xd}|1xo2Y z>H8;fenk2{A_My*^&y#Pl8JpXzE7U{ket~k)B9v{pPc!JJ@hD?+_k4}$~@H{j9#a7 T;4v{#7f7IY*WdTL$K3M&2+W4C diff --git a/app/engine/strategies/__pycache__/__init__.cpython-313.pyc b/app/engine/strategies/__pycache__/__init__.cpython-313.pyc index 5771c35684cf2d3be595be6840aaefa9e9168af8..13deebbdcdf962bee1dce54ad9764df47f2b4f29 100644 GIT binary patch delta 54 zcmZ3+xRjCmGcPX}0}!OWkDSPDEbroC6%(FclwDkqn4B6DP+5|ZpBGbHlAoNNSXz>w IJ2B1!0L8WvlmGw# delta 55 zcmZ3=xQvneGcPX}0}%AQiI~W3tl;Wm6%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn0|48T64(F$ diff --git a/app/engine/strategies/__pycache__/base.cpython-313.pyc b/app/engine/strategies/__pycache__/base.cpython-313.pyc index 7aa2d8ca38cc848655ca895456613cccae72c61f..8131845b974272c2ab352ae3acf126fd7782b1b3 100644 GIT binary patch delta 44 zcmcaEbWMo&GcPX}0}!OWkIY)Vk++$N(P8pLCK0aUlKkZC#L|-d+|3)9ma_o>JeLmL delta 45 zcmca6bX|z|GcPX}0}vFxi^yu&$lJ`s=s0;IlL&WkNq%y+V`)i#ZsO+kOv~8-MA#19 diff --git a/app/engine/strategies/__pycache__/conditional.cpython-313.pyc b/app/engine/strategies/__pycache__/conditional.cpython-313.pyc index 0e931a44a303fba1d97059a5e7bcee6e3c2e3ac4..b01032d59f3553009a574af18647ed8108e21db8 100644 GIT binary patch delta 59 zcmX>ha$JP>GcPX}0}!OWkIXv0k++*o-rdD2COp3=ySN}RIW;DrvLquvFQ&L8KRG+G Nv?M=w^A5HN%m7(!6@dT% delta 60 zcmX>uazcdnGcPX}0}$A}jmYZW$lJ}P;NfBw6P{m`U0jfuoEj5QS(1^T7ZY5PpPcPj OT9TiexOqF<1ZDtP!WB9I diff --git a/app/engine/strategies/__pycache__/periodic.cpython-313.pyc b/app/engine/strategies/__pycache__/periodic.cpython-313.pyc index 4b87641cd516e9c6a1534cd4c17487e65251151c..31df8d38bd33cbef735db0d2786f1955fb4aaa5a 100644 GIT binary patch delta 60 zcmbO%HA#x^GcPX}0}!OWkIYix+Q|2sL*B*3DkeO?D7&~IF*!9Rpt2+*KQE@ZBtJPj Ov9u&Vce5ntFJ=Hd_7u4Q delta 61 zcmbOvHCc-9GcPX}0}w>NjmUb)xsmTRhk~n%RZMt(QFd`bVsdIsKxIireqKy)Nq%y+ PV`)i#ZsKMM&R@&`Y`qnU diff --git a/app/engine/strategies/__pycache__/technical.cpython-313.pyc b/app/engine/strategies/__pycache__/technical.cpython-313.pyc index bdb50945d18c27719f309e671e4fa4456d58479f..287934c0a70c9067020561b6afea604d660a0ec0 100644 GIT binary patch delta 59 zcmbPkGtGwgGcPX}0}!OWkIeeBk@p{~ysL{ dict[str, int]: + """실제 계좌 잔고 기반 보유수량 맵. 실패 시 로컬 DB로 폴백.""" + if token_manager.is_authenticated: + try: + balance = await account_service.get_balance() + holdings = {s["stock_code"]: s["qty"] for s in balance.get("stocks", [])} + if holdings: + return holdings + except Exception as e: + logger.warning("잔고 조회 실패 - 로컬 보유정보 사용: %s", e) + + from app.models.stock import Holding + + return {h.stock_code: h.qty for h in db.query(Holding).filter(Holding.qty > 0).all()} + async def evaluate_all(self, db: Session) -> list[Signal]: - strategies = db.query(Strategy).filter(Strategy.is_active == True).all() + strategies = db.query(Strategy).filter(Strategy.is_active).all() signals: list[Signal] = [] + real_holdings = await self._get_real_holdings(db) for strat_record in strategies: try: @@ -55,7 +73,7 @@ class StrategyEngine: continue price_history = self._get_price_history(db, strat_record.stock_code) - holding = self._get_holding_qty(db, strat_record.stock_code) + holding = real_holdings.get(strat_record.stock_code, 0) signal = strategy.evaluate(current_price, price_history, holding) if signal.action != "hold": @@ -103,7 +121,7 @@ class StrategyEngine: qty=signal.qty, price=signal.price, order_type=settings.strategy.default_order_type, - status="filled" if order_result.get("rt_cd") == "0" else "rejected", + status="pending" if order_result.get("rt_cd") == "0" else "rejected", strategy_id=signal.strategy_id, ) db.add(trade) @@ -136,11 +154,6 @@ class StrategyEngine: for r in reversed(records) ] - def _get_holding_qty(self, db: Session, stock_code: str) -> int: - from app.models.stock import Holding - holding = db.query(Holding).filter(Holding.stock_code == stock_code).first() - return holding.qty if holding else 0 - def reset_daily_count(self) -> None: self._daily_trade_count = 0 diff --git a/app/models/__pycache__/__init__.cpython-313.pyc b/app/models/__pycache__/__init__.cpython-313.pyc index 62e09d44b333ace7b50c16ae9d1125da0187cd6e..1e523c1f0998fc1169322bbc1355ec452c51c28a 100644 GIT binary patch delta 54 zcmbQuIE#_{GcPX}0}!OWkDSPDEN|&z6%(FclwDkqn4B6DP+5|ZpBGbHlAoNNSXz>w IJ2B1{0KcaaXaE2J delta 55 zcmbQmIGd6CGcPX}0}yn*iI~W3tYGD06%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn768=`5~=_I diff --git a/app/models/__pycache__/stock.cpython-313.pyc b/app/models/__pycache__/stock.cpython-313.pyc index 0726163745e68b2797326b0d098144ef70c3a8fc..6e4b8015d991084f6fb12919954276218414a438 100644 GIT binary patch delta 59 zcmX@7aaM!(GcPX}0}!OWkIXu^k#|3{yqSwtOn81#c5y*sa%xOKWl2VUUQBUGesXqV NX-R(W<~Phq0sw2l73=^2 delta 60 zcmX@BaZZExGcPX}0}z~h6Oq-lk#|3{g1L)TOn81#c5y*sa%xOKWl2VUUQBRFesZ>B OX-R%=;^x=PNdf?bVHPF; diff --git a/app/routers/__pycache__/__init__.cpython-313.pyc b/app/routers/__pycache__/__init__.cpython-313.pyc index 65ea5075ad50f74462034bfb38ec707d060e01b6..40a74fe4d02305777adba94680b1fd5848e82dce 100644 GIT binary patch delta 54 zcmbQkIGd6CGcPX}0}!OWkDSPDEN|sv6%(FclwDkqn4B6DP+5|ZpBGbHlAoNNSXz>w IJ2B1<0Kha7YybcN delta 55 zcmbQuIERt@GcPX}0}%ARiI~W3tYGb86%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn4gl6i60!gQ diff --git a/app/routers/__pycache__/dashboard.cpython-313.pyc b/app/routers/__pycache__/dashboard.cpython-313.pyc index 727a67a996cca209d79157f25d4a35f2b3500b93..27e316a7031b23876e649a50add8d91d34d79fb8 100644 GIT binary patch literal 5002 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 literal 4320 zcmd59TTmO<^{(CzguoK=N?;HV+X#aQ)Hb%`*u~(QNCw+l%S|mhnnhYbwpii2D+W4~ z2~9tmd?lXNwV8MZGSiuqOlIO_`jO@*^;g_K6eX5=2T#*!IvxKRlXk{`J$F~Tf*AbM zpYBNao_p@ubI&>Vymr&&av*p<*?jHVH+F>nK^NL%Rsm-7&j8#)9O9TLiZGZ7QrHkR zM2y%NF=3OkHb%`63$`e*DQb<_uuXx@QG3LJ9SUrTIwLi>CgQ@bNG-07xUoA@hwGF* zYqUPH2k!}@abp|eY@EH9;T*>e*fZ{lg;Ytlj-2xdqprtIZD`z~tqwC>Emspj*G$)# zBb66$!;~xJeoR*(hB$dq%*ni*$%#_PK5%rmWfsFQ^V6)vyjN_!o{Ld>s-J~e)uGGs+i(U z+xKIx?Q6Qy&egfPpQqs%efLF@8+MVrPUEO^@o1RpoY1La-YQmuIKzn%khNmn7uwIb zixfVMm(FD;DRip;Q3_MSubsyk11QdTp0l)Q-a=#Ag~qal#=HxSwHJxDL_j+))}%qz zxw8WD*TEmFl+9HN4QkWn^q`R#LZUrxi?wRI)j4jDwN+Od1lSG@uFilxP+e(|E>i=@ zr012LX|+4Mz(BsUgpJxdh(<3X2V^+cs=0daFTV)Tl$Gxa3)O(&K|bX8@}R@482G)faN%_oFndty`-I6odMfl zU1<5NC zvwTVzzqlYz=frWPh7IQB+>BB?$NAaWajX^6%##`nx}! zezs`?>Nh>dnWfaMUB?@sG-?^5p{20uQ#diG(U@AG5NFs%R~BQa#s#3b@F0D?>k*O-`%Lm_%ySwd&m3ZbqCFBw0ukx4J9L@!33dX=&nA36r#EBlUbr&ddo( zNp0aWf<#<83Ys_x&T~MUm%5%z<)F2zz*IWb0xi@G?R22mj%is4S#bx2X*nP!DU()7 ziCOg?iv-5`~Lx(UOXxw4y zclZzt#H2`Tgb~wTi7>K6trFbwsL@M1QcMS`u`~uzzlgqd*S|CW_WWII(H$(gI|}ZO zqWi$&NZISVJ#us8Zu~bFO5W~*xBH`bsppkKν#GliZr#h&4!_iV`q!|9p`hEwPsh?4=^ht(h9O z9n@9uc71f@j># zJJGkJWuO1{}P`~%-Lx2|-)H&brvEH`(TTf*hx*joHbarnxe^M$74pACK0bb4+4=j*K< zYx|EDTL;$n1y}YJ_w{bLZC=O50px4mY;ZO>o_Ya#mw8f;T7t{jrEIymMVa=pxAQmi z<$cY|iKRrjwe5qJ_gl(=#^uhX&N5KGzV!O{ZkwxiV-LK(K^bjyBVRkP3@9wy6_&sx zeKW1GWc|yQB}=)&*&kTmyR=thdE(_Sy1(eY!_%`%&4&xkhrez<`e0Z;qaDr&IG(Vm zv1Q{N(@|r2f;!BWXHR`_^0TKb>`BzG9vBA$(GJ^R+6P~bh8$xd6OK@~qu*1+f(1E` zMF`MLN)GvmBbl7c14c+DF|CBeI>|%kn9X2XEHM-;1UcYFme0aB=FCDE0z+lwg3t`7 za##q{s8eIGUdN?*nV;Y#0n?I0>L`8&5?fXZD_Ks-9+);mN^($Ah?)+(lvEI>GNKUH zQ;(V-ENWjsjF29b7+OV}zyT?dMQkVG0Qd$zg%f~d;4fW(;fax9m`AAXU+4gg|3n8L zp^m?y$TukR2zCA4zGp4ab??xjP$yMQk1$0v3@&Tpk+(R*^@%8-y4SkSZjT z7fR+H3fXD^-X~cXkSrl%7fsp%09EGI9M)P(v{fP7h*0n-l@i|t3;U4H143+Z*9oOM z$>svn%rxwJ8h{))M@MNi!ASuABT2nfS(c<4Cwa9z3C>YfEd7s08YFvJj@pus!S%Hz zIf_Z7M#)KnwBB7MA(}rfTdhSjNp7j8tWe#*MAd3hje_KQrcnJPB{Xl65(3Pa$73;B z8CRy{SbSLJsb8U0{fknY4j+F#6qM0Se0Dq%4V`>jIVZuUbQ0Y|Md2uH1x7Gj-3-PU^>agg`xsMj0bs;fM0@W}$^gc38n^nX*^!(8N9ZOn z0e$oY6!gQJyZGWfX~N!t6Owy-{^q0m^X~0i*B)JY;NHG+b88XrzF62=`Wn50eNZnJ z2|E_pwmrY>-oCxKbK@%Zyt{Mto}fph+l*~8s)Qp6grYGeJd36x(Twh#qRd{*@NYIu zU5LsFrT=0iI!n$o!}iIsSTv&aBP1i0;`S#8a1K9y;xkxmf;o_7Xk8F)IEA#ccg@+m zEUr5T()_@J?sq+4Z5v$OJv!AXu5y8tG4P|ApBs8?2DU-CyTRJhtY?+=-0e@dcCWT} zr_8<_fFxHTW^|Q1l`@{n(nR-XZXd3933~hjoUiqPTd<4ygz4!r5$e1nzLv51pv;tU4y^H`Ly z!qt&>^{%-HD_jF<`@lj4VTGfd74{Tk$Fy*~5G_o3UZ6o-QC;DEFsS~)yT^j09ny-x z6mD20QS52qW+O4`ON`{cW$3AxQgAy>>3g0naah{aKx^{A$<$Z8;iHEm;bqtQ}Cs%dQVxDYkhXdY(`f aniZpLjb`1q4%>1#!a-u&9}|Q~*M9?Y9=zQE delta 1174 zcmZuwO-vI(6rS1HcKf^SE(J<@uHu@A;d4S)ogM%_}f>MCgraONM8 z5LVWRR4|eHRntoltIMnZ(Wu5{YczuYc^SSR2bM+fI$X|&R=ijJXM|5wtW2!O8*t^C zMWLQxv}TT5jKYFDoyla)wAq`@( zS91O7E+g4--MpO5B(a(8x{}D7*=uQ9M4MTwBHn@lIws~}V8Ubj1!2{MO}dI;?F4b% z7GRA2^4w8vZ1+t7;9@@mn?M+h(qP%~6sN6&Ar>%-3kEz3Hv! zHM1wzz``6D1fSI{GiuXYwduKQT0Ju3IWpM#(e0m=tDmB&2JMZkPdW7WZvWu%?-KB~ zz~4C!8ypHX!iTUyd=zY?FMT_NuYeYPkLg{1o85A1qs_`rXVgwwSTkv*x0Twms6-C( ztc`~y{++Vdb%Nzb*^}#}irO5X<$hvs)!4JX*JaZxp7G$p){+4Pyl0pJl{F6~Y7=|m zC1RRQ&8%u?G)Uau~= zQmO8|nI}dnMGze;e--E7k8sPRk_WCsYFH>Z4qVG#%3n1OkxrKK|1bBAg#`p5`~V_f p=>3WpyG5uhA|Py95ExpJf#A3kx)GZ4Y@CLXpMrg<`kf<7`VXCz>*fFe diff --git a/app/routers/__pycache__/strategies.cpython-313.pyc b/app/routers/__pycache__/strategies.cpython-313.pyc index 368de5a2d1be4b33299bb515772c212726898dbb..a87c18f7c73fe453ccc8f82368872144aab8eb80 100644 GIT binary patch delta 59 zcmdm{wN;DvGcPX}0}!OWkIcHTk#`rfyp4-hOn81#c5y*sa%xOKWl2VUUQBUGesXqV NX-R(W=I6}*f&gLl6|4XN delta 60 zcmdn0wM~onGcPX}0}!lw8B OX-R%=;^t?}{(=B+brq-p diff --git a/app/routers/__pycache__/trading.cpython-313.pyc b/app/routers/__pycache__/trading.cpython-313.pyc index 882fd04594a2f9901e70823ea288202216007c80..276ea6462b0bd979f021a754856017a62d8ed777 100644 GIT binary patch literal 5746 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 mj*lTbuctjT!NI5@WZXDl@p z!f=5i;9!kV5`+kFed1#(Up(QlfUEy#2rB|M%bb z=E3E?`|EDEgTVLU-!IJnVkhJ`WLlrtAh`PbmHoZWa{?7&L?RMDR(bAdd7AGdl1;J? zaFXM=g?hRP@Xj!iT+?kyzOj;enbZx^_6MZCX-{f@E66sMI+9$2o3{3mX=jTY;-rw| z36pv2JeO>mQz3v(DtDu0DJ=O=xupt%=Fqm=7A5&x4yAw;+!2EBArJmZ5vg-Wz^+Gl zEq86XjY{4BOQid9AinD_j34Jrr<~6hbXm_9@>-lX?IRP+T@ll5dO%b#kLF*xLH)D-at1{Dxqz`~;E6|Ke z)A<7Rqb!JkY0>$3T%-|{L=o&l(1V~CK@0&}QRcW0ss0w|G~7bC5wZXCL&85?nYj<3 zp1E(7f$Tm2CNTx7rJ@$g&=A;Y_A3)yH|%-hCjM|Gm^Nm z=Vm1N*3~bEjSp<~Da^a!5DSV6jd(6a&MRsz&9-$rZB}&LLLb9L*uQ!!K(MjlqJ=?LrmKQ7ttg8TD_VQT9R$NWo3+rK}*VR zeC-M!Z7s<2Q{+I?R))-`_!Ms9j&Iw=x)3zpb#(|l70tQqfOk?#IYvsLk#AhIReJJ z4!Z#DIK?)s3xatBJeTx zvCcESIAoYQJ(q=>1xb&B$&>J}anNM15#y|Py1Wr1zV3Rj$0cs|0Cjz&79sw?Qh%kt z+8tVTtT?LS$m+qBgDn&YE}gBMt%6CilB@dsibXk=uiz#Cj-kD>V4caC})sk6RlTnT@+=K}{zBz?j1AzXlVE#$Jhy7xHT3 zB-l{haz$~$HAfD%`yx8K1Y=>_N62)P)9Kj~1XI&#ic`RBo0Vax6|>X|9K}mb@f=NC zaWNz3^{hg%bhBN%l9O}FoVu`>fTo}-z*lC&Q>5(iVs8kW?WVO@fa-8MvUx=Zk;0B)+qdI6Fl{FwLMf-7qA!98 z&MfU7fEv$n+#S;QCrPf8uj^dF=j;2kpftKGMpxM92z)L!!XfA)oz_Zb7Vwh$*) zJ>eP;_^z|PCIGI9#O`65nvIB_`*x7kd1C3VSx|e7SZr^GUk_hDca!U?TP)lFQ*I!| HUabEBs3Gog diff --git a/app/routers/__pycache__/websocket.cpython-313.pyc b/app/routers/__pycache__/websocket.cpython-313.pyc index 523aa4cbbdba1bd07be0f098fbb462da705e7041..5b0ea6ca4902a3305b8fb9f60857fa06badf66b8 100644 GIT binary patch delta 59 zcmdljw@Z%qGcPX}0}!OWkIeGl$g9gNZ|!0g6P{m`U0jfuoEj5QS(1^T7gJo4pPZdo NT9Ti;Ig0rqHvlsN6pjD@ delta 60 zcmdlbw_A?)GcPX}0}yO{8l!N$cZCOp3=ySN}RIW;DrvLquvFDAGoKRMg6 Ov?M<_adRZ|Lv8>@j}){3 diff --git a/app/routers/trading.py b/app/routers/trading.py index 96fbbf2..e9daf86 100644 --- a/app/routers/trading.py +++ b/app/routers/trading.py @@ -46,7 +46,7 @@ async def place_order(data: OrderRequest, db: Session = Depends(get_db)) -> dict qty=data.qty, price=data.price, order_type=data.order_type, - status="filled" if result.get("rt_cd") == "0" else "rejected", + status="pending" if result.get("rt_cd") == "0" else "rejected", ) db.add(trade) db.commit() diff --git a/app/services/__pycache__/__init__.cpython-313.pyc b/app/services/__pycache__/__init__.cpython-313.pyc index 6cf3df86ac3d778fd1aab9b4476070cf5a9f47e3..1f10dec51f808dc6fa8a86808c4d4b37bbefd389 100644 GIT binary patch delta 54 zcmbQsIERt@GcPX}0}!OWkDSPDEN|^%6%(FclwDkqn4B6DP+5|ZpBGbHlAoNNSXz>w IJ2B240KmZ#Z~y=R delta 55 zcmbQkIG2(8GcPX}0}yn-iI~W3tYG6}6%(FclwDkqn4B6DP+5|ZpBEEclAoOISXz>w Jn>aDn9st%^60-mR diff --git a/app/services/__pycache__/account.cpython-313.pyc b/app/services/__pycache__/account.cpython-313.pyc index 84d7e58bb3446a74038369a9cd5ea5c54228d653..7e1b2ea22269fcd7ed418c4d436af92213e78d3c 100644 GIT binary patch delta 45 zcmexk{M(rCGcPX}0}!OWkIV`X*vMzZ%xE>)ky(VRxFkP0JF&DRKX-Ei^Bge%Li`Rf delta 46 zcmexu{KuH@GcPX}0}%Lsj>x*pzmd;~nbCT(BeMv1a7li0wqt2Yes1FCc;-1`09&&T A%K!iX diff --git a/app/services/__pycache__/market_data.cpython-313.pyc b/app/services/__pycache__/market_data.cpython-313.pyc index 9347c6e92fc6b933bac530cb61a40b7ec6a185ba..33951f04a56c1f9198b8b578ae159ce982a2c52b 100644 GIT binary patch delta 2231 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 diff --git a/app/services/__pycache__/realtime.cpython-313.pyc b/app/services/__pycache__/realtime.cpython-313.pyc index 4bc79323888b5ea0a85ef6ee592e4ea1bf4a58e1..caffecb8dcc514df521f2e85f93edd4c21f7bde0 100644 GIT binary patch delta 2135 zcmZ8idu&rx7(eIU-agiL?fTf-J-e<9I>sEb0eh6;9*GX<9+xpEBTH%TMz^kap5Bc| z2s4f^L~GO#6SFNQ{-JS50;XAj=wBGrkxiof<@rvh&Yh&c`#azH z&i9@Bo!|X>?$F{u?uyl#gV-4UePFbq_~YDjc=`=Cy^IDC!7?I(jEN}QWT6m^>WFSo zlI^kxydHM3LG*H-tmCT&<|lDxlpV5D)&n2K#KdQYwOJ1HGIB4o?2=8KS!B1I! zY&|1Rl0*h8DIJ^m*x;ula4VUDjfYx0?-tr(+-a!W`%T5Iig5b0@h1V-(jl{o>*@FA za=e2+HW%BWDkKQd2+%}JEPmWc_gX@@A$`X3vry6sIq^@kVV?BeZWab=h z`cxFCdh|_zlj=5y)ZCO1sg|sEqloI0w z%@~eHNHU%rAYVU zF|dEl;2~4=vY@ASSIC7;2k?@`F%v!?UM$*o$FhIP=AkpLl7XiW%(j`(OlZmBo!xe6 z+f{tq;lFF~-?I3Z1<~5{V}9Xm;$ng(@7e=D*aLqqIg3{iwl*!<^Jgs=EsG@$ckGSJ zBC|YQHX)1muhcBdd=|G0^fTAi>2?9%47TQqa}@%kbDK=SYw}x7;=E2`mU)xJEc3bB zfH&dRJ-Yd3urCPCwqms4#%&(mLcRpNh{ePU#bs@K#Dyk^wh+R=_vjdpuBnCPM##W^ zuR{ZI{;k0kWo(^tSGZk?sxhU55S5T9vj)xC??QnPhXB^4hFyBdQ^Fcjjxwbr zwIS`~ghICR6{4gj6B=ouk3C)-p=NIYpGwzy?@Ra?wU>>Uw?jDo!6oR~vUk_b$7r>$ z8h6tHUulSchocY>$2atxBv^|!_MCNZ{#N=^9~Ha#k$Idr+YvPVr$!QkGeFPH=+p@% zxv^Pr0dHu;q=C)`0{Dycmw^ui{aUXY+1!NZ=zE*LofgPDJls)LO|Y&~1rsr)5ndDm zO_Mv2D~ts($%ThLTZXH!$eRym5GU)VBfS0_tom$^;#OWDT`V4TYdriZGvk`?v@fhC zm0%uyvZb*hW1JxDC5d*DY=Y3$0MNHdKfn=y-2f2)C@yILc$>ZytZ&}O=sC117^Gdx z4y4ar5wNs#MPzIl*s2~&EvtH)>)Sk)vA3(DFHw87;#KU$W?LM8MuY) Ge8b<`u=slb delta 1631 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 delta 2151 zcma)7U2IfE6uxu+_P6`f?oYR{?NSQuwPm{nS|~*+<)`@9qL+nY7q7Rwx7%&YF3jCk zh*8i76XO#ZeA6d=z=%fDn3!Oq28=#v0-6|dW7GsgV&cORLU%%kM#WCHd`}HP0ke2nJ9@4{l zGv_U7kp^3ZPC<{f33_x;Yy{CzF4QRu-b+QpMNx~LxptXdA$_7IGmp5MZ4`UioVa%A zEI7R@hz5^N>53G*_QVM~cK1-6zsQ`?1hK@YHR#CR~MD`=Z1)CIN>+J?ir zde7#DBf5Bu2Qs=j^XeYzD|)pSCvbq8g;KewTlD2p-uf%LG%a`H{ zR&M{v>RhFq)2o&I!p>T?a@1t+$va4F=~vlJ{A=OlHUUV{b*!%Rkv5i8yGak*qh3(s zV5iOOuDYSE3#cR8rGn+iM=J%(mM|uTQM+7^krXStwnNm%t^~QkGLa0s?FzAS`rAm3K4u{r9s>gMpn_4ZBv3^`UGLcy1cwTNpw|rrPZ=~+Rc9KT+Kd@u3=)b zhy4@H5D)8*EhyfMFetK&uT6ylNkD&(t!LlH;!@KfVPD0Ps{+f(vLq{JrK}8_2GgE$ zQ`XNeB%|zZ%|$$@i$`C;`82}Aa?u>xjsPqx#e0g&^{`U(Mf60SsDZ$*^%61@VFn=& z@TfovC;!Lh)yyweY>PhhRU1yH#LomaogQkG6kpPWCSqe& zYqadh6P2>1iL?fGY~{+LCO2B1i-Be?3%H2L@yc)=UTW^ap9g1Hc}FUhtBr?p0^RYo zG-R!v!ezNo%2zeAoQ4;K2R}&21?0i}y*-)+6P^Un+>Q48&uD&h?&){jhEZm}^bd5b z$8g@5{3BAp=^{b}!DhXgWTFS@I)ubBM1j~JFf)@H9adt0MB;S?e(S0bY_7YIxrIQu z?m^}iLM?S4GQSXQtv4YH2;RW$AhHvLeV-X!cV~hSrHk^Na1*WB@fyZ#4J5G)gW}%{ zO|s(yRusQZ^f1B#!bXGys}DriVm%uf@T`t|JNz@%7Of}fTQJ4H7<-oWZ0z3wr;Mh~ zt@_$`< 0: + if order_amount < settings.trading.min_order_amount: + return { + "rt_cd": "-1", + "msg": ( + f"최소 주문 금액 미달: {order_amount:,}원 " + f"< {settings.trading.min_order_amount:,}원" + ), + "order_no": "", + } + if order_amount > settings.trading.max_order_amount: + return { + "rt_cd": "-1", + "msg": ( + f"최대 주문 금액 초과: {order_amount:,}원 " + f"> {settings.trading.max_order_amount:,}원" + ), + "order_no": "", + } + await self._rate_limiter.acquire() tr_id = self._get_tr_id(side) @@ -237,22 +267,24 @@ class TradingService: else: side = "buy" if side_code == "01" else "sell" - trades.append({ - "order_no": item.get("ord_gno_brno", ""), - "stock_code": item.get("pdno", ""), - "stock_name": item.get("prdt_name", ""), - "side": side, - "qty": qty, - "price": int(float(item.get("ft_ccld_unpr3", 0))), - "order_type": item.get("ord_dvsn", "00"), - "status": "filled", - "order_amount": float(item.get("ft_ord_amt", 0)), - "settlement_amount": float(item.get("ft_ccld_amt", 0)), - "tax": float(item.get("sttl_evlu_amt", 0)), - "commission": float(item.get("ft_lof_ruse_amt", 0)), - "created_at": item.get("ord_sttm", ""), - "filled_at": item.get("ft_ccld_no", ""), - }) + trades.append( + { + "order_no": item.get("ord_gno_brno", ""), + "stock_code": item.get("pdno", ""), + "stock_name": item.get("prdt_name", ""), + "side": side, + "qty": qty, + "price": int(float(item.get("ft_ccld_unpr3", 0))), + "order_type": item.get("ord_dvsn", "00"), + "status": "filled", + "order_amount": float(item.get("ft_ord_amt", 0)), + "settlement_amount": float(item.get("ft_ccld_amt", 0)), + "tax": float(item.get("sttl_evlu_amt", 0)), + "commission": float(item.get("ft_lof_ruse_amt", 0)), + "created_at": item.get("ord_sttm", ""), + "filled_at": item.get("ft_ccld_no", ""), + } + ) logger.info("일일 체결 내역 조회: %d건", len(trades)) return trades diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..68e96c1 --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,43 @@ +# StockAutomation 설정 템플릿 +# 사용법: 이 파일을 복사하여 config.yaml 로 저장한 뒤 수정하세요. +# cp config.example.yaml config.yaml +# +# 주의: 시크릿 값(app_key, app_secret, account_no, hts_id)은 이 파일에 두지 마세요. +# 반드시 환경변수(.env 또는 서버 환경변수)로 주입하세요. +# KIS_APP_KEY / KIS_APP_SECRET / KIS_ACCOUNT_NO / KIS_ACCOUNT_CODE / KIS_HTS_ID + +app: + name: "StockAutomation" + version: "0.1.0" + host: "0.0.0.0" + port: 8000 + debug: true + db_path: "./data/stock.db" + log_level: "INFO" + +kis: + server_mode: "vps" # vps: 모의투자, real: 실전투자 + rate_limit: + requests_per_second: 2.0 # 모의투자/실전투자 공통: 초당 2회 + retry_delay: 1.5 + +collector: + interval_seconds: 5 # 주가 수집 간격 (초) + market_open_hour: 9 + market_close_hour: 15 + market_close_minute: 30 + +strategies: + check_interval_seconds: 3 # 전략 체크 간격 (초) + max_daily_trades: 50 # 일일 최대 매매 횟수 + default_order_type: "00" # 00: 지정가, 01: 시장가 + +trading: + max_order_amount: 10000000 # 최대 주문 금액 (원) + min_order_amount: 100000 # 최소 주문 금액 (원) + slippage_percent: 0.1 # 슬리피지 허용 비율 (%) + +logging: + level: "INFO" + format: "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" + file: "./data/logs/stock.log" diff --git a/data/stock.db-shm b/data/stock.db-shm deleted file mode 100644 index fe9ac2845eca6fe6da8a63cd096d9cf9e24ece10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeIuAr62r3