Spaces:
Running
Running
Update final_sell_monitor.py
Browse files- final_sell_monitor.py +123 -3
final_sell_monitor.py
CHANGED
|
@@ -21,6 +21,7 @@ import sys, ssl, os
|
|
| 21 |
from datetime import datetime, timezone, timedelta
|
| 22 |
import FinanceDataReader as fdr
|
| 23 |
import confirm_sell_backtest as BT # ํต์ฌ ๋ก์ง/์ค์ ๊ทธ๋๋ก (main()์ __main__ ๊ฐ๋๋ผ ์คํ ์ ๋จ)
|
|
|
|
| 24 |
|
| 25 |
KST = timezone(timedelta(hours=9))
|
| 26 |
|
|
@@ -51,11 +52,115 @@ MAP_PATH = os.path.join(HERE, 'market_map.csv')
|
|
| 51 |
# =====================================================
|
| 52 |
# [์ฌ๊ธฐ๋ง ๋ฐ๊พธ๋ฉด ๋จ] ๋ณด์ ์ข
๋ชฉ ์ฝ๋
|
| 53 |
# =====================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
WATCH = [
|
| 55 |
-
'277810', # ๋ ์ธ๋ณด์ฐ๋ก๋ณดํฑ์ค
|
| 56 |
-
'373220', # LG์๋์ง์๋ฃจ์
|
| 57 |
# ์ฝ๋ ๋์ ์ข
๋ชฉ๋ช
์ผ๋ก๋ ๋จ! ์: '๋ ์ธ๋ณด์ฐ๋ก๋ณดํฑ์ค', 'LG์๋์ง์๋ฃจ์
'
|
| 58 |
# '060370',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
]
|
| 60 |
|
| 61 |
ALERT_DAYS = 5 # ์ต๊ทผ ์ด ๊ฑฐ๋์ผ ์์ ๋งค๋์ ํธ๊ฐ ๋ฌ์ผ๋ฉด [๋งค๋]๋ก ์๋ฆผ
|
|
@@ -164,7 +269,9 @@ def _skip_reason(stock, low, ma240, s):
|
|
| 164 |
if getattr(BT, 'SKIP_IF_NO_240', False) and cat == 'UNKNOWN':
|
| 165 |
return '240์ผ์ ์ด ์์ง ์๋ ์ข
๋ชฉ(์์ฅ ์ผ๋ง ์ ๋จ)์ด๋ผ ์ ํธ๋ฅผ ๋ฏฟ์ ์ ์์ด์'
|
| 166 |
if getattr(BT, 'SKIP_IF_BELOW_240', False) and cat == 'ALREADY_DOWN':
|
| 167 |
-
|
|
|
|
|
|
|
| 168 |
med = getattr(BT, 'MAX_EARLY_DAYS', None)
|
| 169 |
if med is not None and cat == 'EARLY' and days is not None and days > med:
|
| 170 |
return f'์ ํธ ๋ค {days}๊ฑฐ๋์ผ์ด๋ ์ง๋์์ผ 240์ผ์ ์ด ๊นจ์ง โ ๋๋ฌด ์ฑ๊ธํ ์ ํธ๋ผ์'
|
|
@@ -214,6 +321,15 @@ def check_one(tk, market):
|
|
| 214 |
stock, index, r['dates'], r['start_pos'], return_state=True)
|
| 215 |
# ๋ฐฑํ
์คํฐ์ ๋์ผํ ํํฐ๋ก '์ง์ง ๋งค๋'๋ง
|
| 216 |
sells = _real_sells(stock, low, ma240, raw_sells)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
last_i = len(stock) - 1
|
| 218 |
recent = [s for s in sells if s['i'] >= last_i - ALERT_DAYS]
|
| 219 |
|
|
@@ -241,11 +357,15 @@ def check_one(tk, market):
|
|
| 241 |
'trigger': trigger,
|
| 242 |
'skipped': skipped,
|
| 243 |
'state': state,
|
|
|
|
| 244 |
}
|
| 245 |
|
| 246 |
|
| 247 |
def status_line(res):
|
| 248 |
"""ํ์ฌ ๋จ๊ณ๋ฅผ ์งง๊ฒ: ๊ด๋ง / flagON ๋๊ธฐ / 1์ฐจ / 2์ฐจflagON ๋๊ธฐ + ์ด๊ฒฉ."""
|
|
|
|
|
|
|
|
|
|
| 249 |
st = res.get('state')
|
| 250 |
if not st:
|
| 251 |
return '๊ด๋ง (์ ํธ ๋๊ธฐ)'
|
|
|
|
| 21 |
from datetime import datetime, timezone, timedelta
|
| 22 |
import FinanceDataReader as fdr
|
| 23 |
import confirm_sell_backtest as BT # ํต์ฌ ๋ก์ง/์ค์ ๊ทธ๋๋ก (main()์ __main__ ๊ฐ๋๋ผ ์คํ ์ ๋จ)
|
| 24 |
+
import buy_engine as be # ๋งค์ ์์ง (์ผ๋ด ๋์ฐ/์์๋ ๊ตญ๋ฉด โ '๋ชจ์๊ฐ๋ ๊ตฌ๊ฐ' ํ๋ณ)
|
| 25 |
|
| 26 |
KST = timezone(timedelta(hours=9))
|
| 27 |
|
|
|
|
| 52 |
# =====================================================
|
| 53 |
# [์ฌ๊ธฐ๋ง ๋ฐ๊พธ๋ฉด ๋จ] ๋ณด์ ์ข
๋ชฉ ์ฝ๋
|
| 54 |
# =====================================================
|
| 55 |
+
# =====================================================
|
| 56 |
+
# ๋ชจ์๊ฐ๋ ๊ตฌ๊ฐ (๋งค์ ๊ตญ๋ฉด) โ ์ด ๊ตฌ๊ฐ์ ์ ๋๋งค๋ ์๋์ ๋ฌด์
|
| 57 |
+
# ๊ตฌ๊ฐ = ๋์ฐ์ฑ๊ณต(์์๋ ์์) ~ 2๋ฒ์งธ ์๋ ์ข
๋ฃ(240์ฌ)
|
| 58 |
+
# ์ค๊ฐ์ ์๋ช
์ ์ดํ/์ฌ์ดํด์ข
๋ฃ/๋๋ธ๋์ฐ ๋์ค๋ฉด ๊ฑฐ๊ธฐ์ ๋
|
| 59 |
+
# =====================================================
|
| 60 |
+
IGNORE_SELL_IN_ACCUM = True # False ๋ก ํ๋ฉด ๊ธฐ์กด์ฒ๋ผ ํญ์ ์๋
|
| 61 |
+
|
| 62 |
+
_accum_cache = {}
|
| 63 |
+
|
| 64 |
+
def accum_ranges(tk):
|
| 65 |
+
"""buy_engine ์ผ๋ด ๊ตญ๋ฉด์์ '๋ชจ์๊ฐ๋ ๊ตฌ๊ฐ' [(์์์ผ, ๋์ผ|None)]"""
|
| 66 |
+
if tk in _accum_cache:
|
| 67 |
+
return _accum_cache[tk]
|
| 68 |
+
out = []
|
| 69 |
+
try:
|
| 70 |
+
df = fdr.DataReader(tk, '2000-01-01')
|
| 71 |
+
if df is not None and len(df) > 300 and 'Close' in df.columns:
|
| 72 |
+
ev, buys, mts = be.run(df, '1day')
|
| 73 |
+
start = None
|
| 74 |
+
for d, tag, dt in ev:
|
| 75 |
+
ds = str(d)[:10]
|
| 76 |
+
if tag == '๋์ฐ์ฑ๊ณตโ์์๋์์':
|
| 77 |
+
start = ds
|
| 78 |
+
elif start and (('2๋ฒ์งธ ์๋ ์ข
๋ฃ' in tag) or ('240์ฌ' in tag)
|
| 79 |
+
or ('์๋ช
์ ์ดํ' in tag) or ('์ฌ์ดํด์ข
๋ฃ' in tag)
|
| 80 |
+
or ('๋๋ธ๋์ฐ' in tag)):
|
| 81 |
+
out.append((start, ds)); start = None
|
| 82 |
+
if start:
|
| 83 |
+
out.append((start, None)) # ์งํ ์ค
|
| 84 |
+
except Exception:
|
| 85 |
+
out = []
|
| 86 |
+
_accum_cache[tk] = out
|
| 87 |
+
return out
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def in_accum(ds, ranges):
|
| 91 |
+
return any(ds >= a and (b is None or ds < b) for a, b in ranges)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
WATCH = [
|
| 95 |
+
# '277810', # ๋ ์ธ๋ณด์ฐ๋ก๋ณดํฑ์ค
|
| 96 |
+
# '373220', # LG์๋์ง์๋ฃจ์
|
| 97 |
# ์ฝ๋ ๋์ ์ข
๋ชฉ๋ช
์ผ๋ก๋ ๋จ! ์: '๋ ์ธ๋ณด์ฐ๋ก๋ณดํฑ์ค', 'LG์๋์ง์๋ฃจ์
'
|
| 98 |
# '060370',
|
| 99 |
+
'483650', # ๋ฌ๋ฐ๊ธ๋ก๋ฒ
|
| 100 |
+
'096770', # SK์ด๋
ธ๋ฒ ์ด์
|
| 101 |
+
'090430', # ์๋ชจ๋ ํผ์ํฝ
|
| 102 |
+
'326030', # SK๋ฐ์ด์คํ
|
| 103 |
+
'035420', # NAVER
|
| 104 |
+
'352820', # ํ์ด๋ธ
|
| 105 |
+
'003490', # ๋ํํญ๊ณต
|
| 106 |
+
'025540', # ํ๊ตญ๋จ์
|
| 107 |
+
'373220', # LG์๋์ง์๋ฃจ์
|
| 108 |
+
'443060', # HDํ๋๋ง๋ฆฐ์๋ฃจ์
|
| 109 |
+
'032350', # ๋กฏ๋ฐ๊ด๊ด๊ฐ๋ฐ
|
| 110 |
+
'005380', # ํ๋์ฐจ
|
| 111 |
+
'003670', # ํฌ์ค์ฝํจ์ฒ์
|
| 112 |
+
'011790', # SKC
|
| 113 |
+
'005930', # ์ผ์ฑ์ ์
|
| 114 |
+
'000660', # SKํ์ด๋์ค
|
| 115 |
+
'078520', # ์์ด๋ธ์จ์์จ
|
| 116 |
+
'206640', # ๋ฐ๋ํ
๋ฉ๋
|
| 117 |
+
'302440', # SK๋ฐ์ด์ค์ฌ์ด์ธ์ค
|
| 118 |
+
'328130', # ๋ฃจ๋
|
| 119 |
+
'005250', # ๋
น์ญ์ํ๋ฉ์ค
|
| 120 |
+
'234690', # ๋
น์ญ์์ฐ๋น
|
| 121 |
+
'083930', # ์๋ฐ์ฝ
|
| 122 |
+
'195940', # HK์ด๋
ธ์
|
| 123 |
+
'122870', # ์์ด์ง์ํฐํ
์ธ๋จผํธ
|
| 124 |
+
'112610', # ์จ์์ค์๋
|
| 125 |
+
'257720', # ์ค๋ฆฌ์ฝํฌ
|
| 126 |
+
'086710', # ์ ์ง๋ทฐํฐ์ฌ์ด์ธ์ค
|
| 127 |
+
'217730', # ๊ฐ์คํ
๋ฐ์ด์คํ
|
| 128 |
+
'003570', # SNT๋ค์ด๋ด๋ฏน์ค
|
| 129 |
+
'194370', # ์ ์ด์์ค์ฝํผ๋ ์ด์
|
| 130 |
+
'131030', # ์ตํฌ์ค์ ์ฝ
|
| 131 |
+
'204320', # HL๋ง๋
|
| 132 |
+
'096530', # ์จ์
|
| 133 |
+
'035900', # JYP Ent.
|
| 134 |
+
'178920', # PI์ฒจ๋จ์์ฌ
|
| 135 |
+
'216080', # ์ ํ
๋ง
|
| 136 |
+
'099320', # ์ํธ๋ ์์ด
|
| 137 |
+
'088130', # ๋์์ํ
|
| 138 |
+
'420770', # ๊ธฐ๊ฐ๋น์ค
|
| 139 |
+
'222080', # SFA๋ฐ๋์ฒด
|
| 140 |
+
'147830', # ์ ๋ฃก์ฐ์
|
| 141 |
+
'281820', # ์ผ์ด์จํ
|
| 142 |
+
'017510', # ์ธ๋ช
์ ๊ธฐ
|
| 143 |
+
'112290', # ์์ด์จ์ผ
|
| 144 |
+
'089010', # ์ผํธ๋ก๋์ค
|
| 145 |
+
'102120', # ์ด๋ณด๋ธ๋ฐ๋์ฒด
|
| 146 |
+
'251370', # ์์ด์์ด์นํฐ
|
| 147 |
+
'094360', # ์นฉ์ค์ค๋ฏธ๋์ด
|
| 148 |
+
'166090', # ํ๋๋จธํฐ๋ฆฌ์ผ์ฆ
|
| 149 |
+
'277810', # ๋ ์ธ๋ณด์ฐ๋ก๋ณดํฑ์ค
|
| 150 |
+
'389020', # ์๋ํ
ํฌ๋๋ก์ง
|
| 151 |
+
'161580', # ํ์ตํฑ์ค
|
| 152 |
+
'232140', # ์์ด์จ
|
| 153 |
+
'033100', # ์ ๋ฃก์ ๊ธฐ
|
| 154 |
+
'031980', # ํผ์์ค์ผ์ดํ๋ฉ์ค
|
| 155 |
+
'403870', # HPSP
|
| 156 |
+
'183300', # ์ฝ๋ฏธ์ฝ
|
| 157 |
+
'445090', # ์์ด์ง๋๋
|
| 158 |
+
'347850', # ๋์ค๋ํ๋งํ
|
| 159 |
+
'030530', # ์์ตํ๋ฉ์ค
|
| 160 |
+
'399720', # ๊ฐ์จ์นฉ์ค
|
| 161 |
+
'042700', # ํ๋ฏธ๋ฐ๋์ฒด
|
| 162 |
+
'440110', # ํ๋
|
| 163 |
+
'039030', # ์ด์คํ
ํฌ๋์ค
|
| 164 |
]
|
| 165 |
|
| 166 |
ALERT_DAYS = 5 # ์ต๊ทผ ์ด ๊ฑฐ๋์ผ ์์ ๋งค๋์ ํธ๊ฐ ๋ฌ์ผ๋ฉด [๋งค๋]๋ก ์๋ฆผ
|
|
|
|
| 269 |
if getattr(BT, 'SKIP_IF_NO_240', False) and cat == 'UNKNOWN':
|
| 270 |
return '240์ผ์ ์ด ์์ง ์๋ ์ข
๋ชฉ(์์ฅ ์ผ๋ง ์ ๋จ)์ด๋ผ ์ ํธ๋ฅผ ๋ฏฟ์ ์ ์์ด์'
|
| 271 |
if getattr(BT, 'SKIP_IF_BELOW_240', False) and cat == 'ALREADY_DOWN':
|
| 272 |
+
tol = getattr(BT, 'BELOW_240_TOL', 0.05)
|
| 273 |
+
dpct = f'{days*100:.1f}%' if days is not None else ''
|
| 274 |
+
return f'์ ํธ ๋น์ผ ์ข
๊ฐ๊ฐ 240์ผ์ -{tol*100:.0f}% ๋๊ฒ ํ์ฐธ ์๋(-{dpct}) โ ์ด๋ฏธ ๋ฆ์ ์ ํธ๋ผ์ (240 ๊ทผ์ฒ์์ผ๋ฉด ์ธ๋ ธ์)'
|
| 275 |
med = getattr(BT, 'MAX_EARLY_DAYS', None)
|
| 276 |
if med is not None and cat == 'EARLY' and days is not None and days > med:
|
| 277 |
return f'์ ํธ ๋ค {days}๊ฑฐ๋์ผ์ด๋ ์ง๋์์ผ 240์ผ์ ์ด ๊นจ์ง โ ๋๋ฌด ์ฑ๊ธํ ์ ํธ๋ผ์'
|
|
|
|
| 321 |
stock, index, r['dates'], r['start_pos'], return_state=True)
|
| 322 |
# ๋ฐฑํ
์คํฐ์ ๋์ผํ ํํฐ๋ก '์ง์ง ๋งค๋'๋ง
|
| 323 |
sells = _real_sells(stock, low, ma240, raw_sells)
|
| 324 |
+
# โ
๋ชจ์๊ฐ๋ ๊ตฌ๊ฐ(๋งค์ ๊ตญ๋ฉด)์ ๋งค๋์ ํธ๋ ๋ฌด์
|
| 325 |
+
accum_now = False; n_muted = 0
|
| 326 |
+
if IGNORE_SELL_IN_ACCUM:
|
| 327 |
+
rngs = accum_ranges(tk)
|
| 328 |
+
if rngs:
|
| 329 |
+
before = len(sells)
|
| 330 |
+
sells = [s for s in sells if not in_accum(s['date'], rngs)]
|
| 331 |
+
n_muted = before - len(sells)
|
| 332 |
+
accum_now = in_accum(r['dates'][len(stock) - 1], rngs)
|
| 333 |
last_i = len(stock) - 1
|
| 334 |
recent = [s for s in sells if s['i'] >= last_i - ALERT_DAYS]
|
| 335 |
|
|
|
|
| 357 |
'trigger': trigger,
|
| 358 |
'skipped': skipped,
|
| 359 |
'state': state,
|
| 360 |
+
'accum_now': accum_now, 'n_muted': n_muted,
|
| 361 |
}
|
| 362 |
|
| 363 |
|
| 364 |
def status_line(res):
|
| 365 |
"""ํ์ฌ ๋จ๊ณ๋ฅผ ์งง๊ฒ: ๊ด๋ง / flagON ๋๊ธฐ / 1์ฐจ / 2์ฐจflagON ๋๊ธฐ + ์ด๊ฒฉ."""
|
| 366 |
+
if res.get('accum_now'):
|
| 367 |
+
mut = f" ยท ๋ฌด์๋ ๋งค๋์ ํธ {res['n_muted']}๊ฑด" if res.get('n_muted') else ""
|
| 368 |
+
return f"โ
๋ชจ์๊ฐ๋ ๊ตฌ๊ฐ (๋์ฐ ์์๋ ๋งค์ ๊ตญ๋ฉด) โ ๋งค๋์๋ ๋ฌด์ ์ค{mut}"
|
| 369 |
st = res.get('state')
|
| 370 |
if not st:
|
| 371 |
return '๊ด๋ง (์ ํธ ๋๊ธฐ)'
|