glorifiedjx commited on
Commit
0c2740e
ยท
verified ยท
1 Parent(s): ee59593

Update final_sell_monitor.py

Browse files
Files changed (1) hide show
  1. 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
- return '์‹ ํ˜ธ ๋‹น์ผ ์ฃผ๊ฐ€๊ฐ€ ์ด๋ฏธ 240์ผ์„ ์„ ๊ฑด๋“œ๋ฆฐ ์ƒํƒœ โ†’ ์ด๋ฏธ ๋Šฆ์€ ์‹ ํ˜ธ๋ผ์„œ'
 
 
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 '๊ด€๋ง (์‹ ํ˜ธ ๋Œ€๊ธฐ)'