Spaces:
Running
Running
johnkim
ENDLOW_MIN_PCT = {'5min':0.010,'15min':0.010,'1hour':0.02,'1day':0.07} # ๋์ง์ ์ต์(60์ ์ด 240์ ์๋๋ก, 60์ ๊ธฐ์ค). ์ผ ํ์
1b4271b | # -*- coding: utf-8 -*- | |
| """ | |
| ๋งค์ ๋ก์ง ์์ง (๋ถ๋ด) - ๋์ฐ/๋์ง์ /์์๋/์๋ช ์ [v4: 60์ ์ต์ ์ ์ต์ปค] | |
| CSV(datetime,Open,High,Low,Close,Volume) ๊ฒฝ๋ก๋ง ์ธ์๋ก. | |
| [๋์ฐ ์ฑ๊ณต ์กฐ๊ฑด] | |
| (1) 60์ ์ด 240์ ์๋ โ ๊ทธ ๊ตฌ๊ฐ '60์ ์ต์ ์ ' = ์์์ (์๋ช ์ /๋น๊ต ๊ธฐ์ค) | |
| โป 60์ ์ต์ ์ ์ ์ต์ปค๋ง (๊ฐ๊ฒฉ ๊ธ๋ฝ๋ด ์๋ โ BBํ๋จ ํํ ๋ฒ๊ทธ ๋ฐฉ์ง) | |
| (2) 60์ ์ด 240์ ์๋ก ์ฌ๋ผ๊ฐ (์ฐ๋ด์ฐ๋ฆฌ) | |
| โป ์ฐ๋ด์ฐ๋ฆฌ ์ถฉ๋ถํ ์ปค์ผ: 60์ ์ด 240์ ์๋ก PEAK_MIN_PCT ์ด์ (๋ ธ์ด์ฆ ํํฐ) | |
| (3) 60์ ์ด ๋ค์ 240์ ์๋๋ก ๋ด๋ ค๊ฐ ๋์ง์ | |
| โป ๋์ง์ ๋ ์ถฉ๋ถํ ๊น์ด์ผ: 60์ ์ด 240์ ์๋๋ก ENDLOW_MIN_PCT ์ด์ (๋ ธ์ด์ฆ ํํฐ) | |
| (4) ๋์ง์ ์ 60์ ์ด ์์์ 60์ ๋ณด๋ค '๋์์ผ' ๋์ฐ ์ฑ๊ณต (๋ฎ์ผ๋ฉด ์คํจ) | |
| (5) 60์ ์ด ๋์ง์ ์์ ๋ค์ 240์ ์๋ก ์ฌ๋ผ๊ฐ | |
| (6) ์ด๋๋ถํฐ ์์๋ ๋งค์ (60>240์์ 60์ ๋๋ฆด ๋) | |
| - 1๋ฒ์งธ ์์๋ ร2, 2๋ฒ์งธ ร1 | |
| - 3ยท4ยท5๋ฒ์งธ... = '240์ฌ' (์ถ๋งค ์ ํจ, ๋ค์ 240์ ์๋๋ก ๊บพ์ผ ๋๊น์ง ๋๊ธฐ) | |
| โป ์ฐ๋ด์ฐ๋ฆฌ/๋์ง์ ์์ผ๋ฉด(๋ ธ์ด์ฆ) ๋ฌด์ํ๊ณ ๊ณ์ ๊ด์ฐฐ | |
| ์๋ช ์ = ์์์ (60์ ์ต์ ์ ) ๋ณผ๋ฆฐ์ ํ๋จ ร (1 - LIFELINE_MARGIN). ๋์ฐ ๋ด๋ด ๊ณ ์ . | |
| 240๊ทผ์ฒ ์ถ๊ฐ๋งค์ = ์ ๊ฐ๊ฐ 240์ ์ค์ ๋ก ๋ฟ์ ๋ (์ ๊ฐ โค 240์ ) | |
| ์คํ: python buy_engine.py 007340_5min.csv 5min | |
| """ | |
| import sys | |
| import pandas as pd, numpy as np | |
| # ===== ์กฐ์ ํ๋ผ๋ฏธํฐ ===== | |
| MA_60, MA_240 = 60, 240 | |
| BB_WIN, BB_STD = 20, 2 | |
| LIFELINE_BASE_MIN = True # [์๋ช ์ ] True๋ฉด '๋ฒ ์ด์ค ๊ตฌ๊ฐ ์ ์ฒด์ ์ต์ BBํ๋จ'์ ์๋ช ์ ์ต์ปค๋ก ์ฌ์ฉ(B์). | |
| # ๊ธฐ์กด(False)์ 60์ ์ต์ ์ผยท๊ฐ๊ฒฉ์ต์ ์ผ '๊ทธ๋ ์' BBํ๋จ๋ง ๋ด์, ๋ฒ ์ด์ค ์ค๊ฐ์ | |
| # ๋ ์นด๋ก์ด ๊ธ๋ฝ(BB๊ฐ ์ญ ๋ฒ์ด์ง ๋ )์ ๋์ณ ์๋ช ์ ์ด ๋๋ฌด ๋๊ฒ ์กํ. | |
| # โป 60์ ์ด 240์ ์/์๋์ธ์ง ๋ฌด๊ดํ๊ฒ WAIT(๋ฒ ์ด์ค) ๋ด๋ด ์ถ์ โ | |
| # ์์ฅ์ด๊ธฐ ํํ์ฐ์ฅ 240 ๊ตฌ๊ฐ์์ 60์ ์ด ์์๋๋ก ์๋ค๊ฐ๋คํด๋ ๊ธ๋ฝ์ ๋์น์ง ์์. | |
| LIFELINE_MARGIN = 0.01 # ์๋ช ์ = ์์์ BBํ๋จ ร (1-1%) | |
| VIRTUAL_LIFELINE = True # [์ผ๋ด] โ ๊ฐ์ ์๋ช ์ (์ฌ์ฉ์ ์์ด๋์ด 2026-08): | |
| # ์ง์ง ์๋ช ์ (์ฒซ ๋์ฐ ๊ฒ)์ '๊ตฌ์กฐ์ฉ'์ผ๋ก ๊ทธ๋๋ก ๋๊ณ , | |
| # ๋๋ธ๋์ฐ/์์๋ ์ฌ๊ฐ๋ก '์์ ์ธ๋'์ด ์๊ธธ ๋๋ง๋ค ๊ทธ ๊ตฌ๊ฐ ์ ์ฉ ๋ณดํธ์ ์ ๋ฐ๋ก ๊ธ๋๋ค. | |
| # ๊ฐ์ ์๋ช ์ = ์ง์ ๋๋ฆผ(DIP) ๊ตฌ๊ฐ์ ์ต์ BBํ๋จ ร(1-๋ง์ง). ์ง์ง ์๋ช ์ ๋ณด๋ค ์์ผ ๋๋ง ์ ํจ. | |
| # ์ด ์ ์ ๊นจ๋ฉด: โ ๊ทธ ์์ ์ธ๋์์ ์ฐ ๋ฌผ๋๋ง ๋งค๋(์ ๋ ์๋) โก ์ฌ๋ฒ ์ด์ค๋ก ๋์ฐ๋ถํฐ ๋ค์ ๊ทธ๋ฆผ. | |
| # โ ๋ฌผํ๊ธฐ๋ ๋๋ ์ ์ฒด ํฌ์ง์ ยท๊ตฌ์กฐ๋ ์ ๋ ๋ฆผ. False๋ฉด ์ด ๊ธฐ๋ฅ ๋. | |
| DEEP_DIP_ONE_WAVE = False # [์ผ๋ด] โ ๊น๊ฒ ๋น ์ง ๋ค ์ฌ๊ฐํ ๋ '1๋ฒ์งธ ์์๋'๋ง ๋งค์ (2026-08 ์ฌ์ฉ์): | |
| # ์ง์ ๋๋ฆผ์ด DEEP_DIP_ONE_WAVE_PCT ๋ณด๋ค ๊น์์ผ๋ฉด, ๊ทธ ๋ค ์ด๋ฆฌ๋ ์์๋๋ | |
| # 1๋ฒ์งธ๋ง ์ฌ๊ณ 2๋ฒ์งธ๋ ์ ์ผ(๊ตฌ์กฐ๊ฐ ์ด๋ฏธ ์ํ ๊ตฌ๊ฐ์ด๋ผ ์ถ๊ฐ ๋ฌผํ๊ธฐ ์ถ์). | |
| # False๋ฉด ๊ธฐ์กด์ฒ๋ผ 1ยท2๋ฒ์งธ ๋ค ๋งค์. | |
| DEEP_DIP_ONE_WAVE_PCT = 0.20 # ์ ๊ท์น ์ ์ฉ ๊ธฐ์ค ๊น์ด(๋์ง์ ์ด 240์ ๋๋น -20% ์ดํ) | |
| VLIFE_GRACE_DAYS = 20 # [์ผ๋ด] โ ๊ฐ์์๋ช ์ ์ดํ ์ ์์ผ (2026-08): | |
| # ์ ์ ๊นฌ ์ฆ์ ํ์ง ์๊ณ , ์ด ์ผ์ ์์ ์ข ๊ฐ๊ฐ ์ ์๋ก ํ๋ณตํ๋ฉด ์๋ ์ผ๋ก ํจ. | |
| # ์ ์ ๋ด๋ด ํ๋ณต ๋ชป ํ๋ฉด ๊ทธ๋ ๊ตฌ๊ฐ๋งค๋. | |
| # ์ด์ : ์ฆ์๋งค๋๋ 'ํจ๋ ํฌ๋งค ๋ฐ๋ฅ'์์ ํ๋ ๊ฒฝํฅ(166090 2020-03-19=์ฝ๋ก๋ ๋ฐ๋ฅ ๋น์ผ, | |
| # 033100 2016-01-20, 236200 2022-06-23 ๋ชจ๋ ๋งค๋ ์งํ๊ฐ ์ต์ ์ ์ด์์). | |
| # ์ง์ง ๋ถ๊ดด๋ ๊ณ์ ์ ์๋ ๋จธ๋ฌผ๊ณ , ๊ธ๋ฝ ๋ฐ๋ฅ์ ๋ฉฐ์น ๋ง์ ๋๋์์ค๋ฏ๋ก ์๊ฐ์ผ๋ก ๊ฑธ๋ฌ๋. | |
| # 0 = ์ ์ ์์(์ฆ์ ๋งค๋). | |
| DOUBLE_MTN_RELIFE = False # [์ผ๋ด] (์คํ) ๋๋ธ๋์ฐ/์ฌ๊ฐ ์ '์ง์ง ์๋ช ์ ' ์์ฒด๋ฅผ ์ฌ๋ ค์ก๊ธฐ โ ์ ์์กฐ์ฌ ๊ฒฐ๊ณผ ์ฑ์ ์ ํ๋ก OFF. | |
| # ๋์ ์ VIRTUAL_LIFELINE ๋ฐฉ์ ์ฌ์ฉ. True๋ก ์ผ๋ฉด ์ง์ง ์๋ช ์ ์ ์ฌ๋ฆผ(์ ์คํ). | |
| _DOUBLE_MTN_RELIFE_DOC = """[์ผ๋ด] โ ๋๋ธ๋์ฐ/์ฌ๊ฐ ์ ์๋ช ์ ์ '์ง์ ๋๋ฆผ(DIP) ๊ตฌ๊ฐ์ ์ต์ BBํ๋จ'์ผ๋ก ์ฌ๋ ค์ก๊ธฐ. | |
| # ๋ฌธ์ (096770): ์ฒซ ๋์ฐ ์๋ช ์ (58,739)์ ์ํ์ค ๋ด๋ด ์ ์งํ๋ค ๋ณด๋, ์ฃผ๊ฐ๊ฐ 15๋ง์๋์ธ๋ฐ | |
| # ๋ณดํธ์ ์ด 5.8๋ง์ด๋ผ ์ฌ์ค์ ๋ณดํธ๊ฐ ์์ โ ๊ณ์ ๋จ์ด์ง๋๋ฐ ์์๋ ๋งค์๋ง ๋ฐ๋ณต. | |
| # ํด๊ฒฐ(์ฌ์ฉ์): 60์ ์ด 240์ ์ ๋ค์ ๋ง๋ ๋๋ธ๋์ฐ/์ฌ๊ฐ๊ฐ ํ์ ๋๋ ๊ทธ ์์ ์, | |
| # ๋ฐฉ๊ธ ์ง๋์จ DIP ๊ตฌ๊ฐ์์ ๊ฐ์ฅ ๋ฎ์๋ BBํ๋จ์ ์ ์๋ช ์ ์ผ๋ก ์ผ๋๋ค. | |
| # (096770: 2022-10-11 BBํ๋จ 127,575 ร 0.99 = 126,299) | |
| # โป ์๋ช ์ ์ '์ฌ๋ฆฌ๊ธฐ๋ง' ํจ(๋ด๋ฆฌ์ง ์์) โ ์ ์๋ช ์ ๋ณด๋ค ๋ฎ์ผ๋ฉด ๊ทธ๋๋ก ๋ . | |
| # False๋ฉด ์์ ์ฒ๋ผ ์ฒซ ๋์ฐ ์๋ช ์ ์ ๊ณ์ ์ ์ง.""" | |
| LIFELINE_KEEP_MTN = True # [์ผ๋ด/PEAK] โ ์๋ช ์ ์ดํํด๋ ๋์ฐ ๊ตฌ์กฐ๋ ์ ์ง: | |
| # ์ฃผ๊ฐ๊ฐ ์๋ช ์ ์ ๊นจ๋ฉด ์ ๋๋งค๋(์์ )๋ ํ๋, ๋์ฐ์ ํ๊ธฐํ์ง ์๊ณ ๊ณ์ ๊ด์ฐฐ. | |
| # ์ดํ 60์ ์ด 240์ ์๋ก ๋ณต๊ทํ ๋ '๋์ง์ 60์ > ์์์ 60์ '(๋ ๋์ ์ ์ )์ด๋ฉด | |
| # ๋์ฐ ์ฑ๊ณต์ผ๋ก ์ธ์ โ ์์๋ ๋งค์ ์์. ๋ ๋ฎ์ ์ ์ ์ด๋ฉด ๊ธฐ์กด๋๋ก ์ฌ๋ฒ ์ด์ค. | |
| # ๊ทผ๊ฑฐ(236200): ์ฃผ๊ฐ๋ ๊ธ๋ฝํด ์๋ช ์ ์ ๊นผ์ง๋ง 60์ ์ ์ ๊บพ์ฌ | |
| # ์์์ 19,898(2017-03-20) < ๋์ง์ 20,178(2018-11-19) = ๊ตฌ์กฐ์ ์ฑ๊ณตํ ๋์ฐ์ธ๋ฐ, | |
| # ๊ธฐ์กด ๋ก์ง์ 2018-10-19์ ํต์งธ ํ๊ธฐ โ ์์๋๊ฐ 2020-01-06๊น์ง 1๋ ์ด์ ๋ฐ๋ ธ์. | |
| # ๋์ฐ์ฑ๊ณต ์ธ์ ์ ์๋ช ์ ์ ์ ์ ์ (๋์ง์ BBํ๋จ)์ผ๋ก ์ฌ์ค์ . | |
| # False๋ฉด ์์ ์ฒ๋ผ ์๋ช ์ ์ดํ=๋์ฐ ์คํจ(WAIT๋ก). | |
| PEAK_MIN_PCT = {'5min':0.01,'15min':0.01,'1hour':0.02,'1day':0.05} # ์ฐ๋ด์ฐ๋ฆฌ ์ต์(60์ ์ด 240์ ์๋ก). ์ผ๋ด์ ํฌ๊ฒ | |
| ENDLOW_MIN_PCT = {'5min':0.010,'15min':0.010,'1hour':0.02,'1day':0.07} # ๋์ง์ ์ต์(60์ ์ด 240์ ์๋๋ก, 60์ ๊ธฐ์ค). | |
| # 2026-08 ๋ด๋ณ ์์ ์ฌ์กฐ์ (๊น์ด๋ถํฌ๋ก '๊ฑธ๋ฌ๋ด๋ ๋น์จ'์ ์ผ๋ด(~37%) ์์ค์ ๋ง์ถค): | |
| # 5min 1.0% / 15min 0.5%โ1.0%(5๋ถ๋ด๊ณผ ํต์ผ, ์ํฅ 420770 1๊ฑด) / 1hour 1.0%โ2.0%(204320 2025-04 ์ฐ ํฉ์ณ์ง) | |
| # 1day 7%. (๋ด ํด์๋ก ๋๋ฆผ ๊น์ด ์ปค์ง๋ฏ๋ก ๊ธฐ์ค๋ ์ปค์ง๋ ๊ฒ ๋ ผ๋ฆฌ์ ) | |
| NEAR_240 = 0.003 # 240๊ทผ์ฒ ๋งค์ = ์ ๊ฐ โค 240์ +0.3% (๋ฐ์ง ๋ถ์ผ๋ฉด ๋งค์). 6/12 15:15(+0.17%) ์ก์ผ๋ ค๊ณ | |
| RECOVER_PCT = {'5min': 0.05, '15min': 0.08, '1hour': 0.10, '1day': 0.08} # ์ง์ ์์๋ '์ข ๊ฐ' ๋๋น ์ฌ๋ฌด์ฅ % | |
| WAVE_RISE_PCT = {'5min': 0.01, '15min': 0.01, '1hour': 0.01, '1day': 0.01} # 1๋ฒ์งธ ์์๋ ์ : ์ข ๊ฐ๊ฐ 60์ ์๋ก ์ด๋งํผ | |
| WAVE_EXIT_PCT = 0.05 # [์ผ๋ด] '์' ๊ตฌ๊ฐ์์ ์ข ๊ฐ๊ฐ 60์ ์๋ก ์ต๊ณ +5% ์ด์ ์ฌ๋ผ์ผ ๊ทธ ์์๋(์๋๊ตฌ๊ฐ) ์ข ๋ฃ | |
| # ์ด์ง ์ฌ๋ผ๊ฐ๋ค ๋ง๋ ๊ฑด(์: +3.5%) ์์ง ๊ฐ์ ์๋ ๊ตฌ๊ฐ์ผ๋ก ๋ด | |
| DOUBLE_MTN_GAP = 0.44 # [์ผ๋ด] ๋๋ธ๋์ฐ ์ํ: ์ด๋ฒ ์ฐ ํผํฌ๊ฐ ์ง์ ์ฐ ํผํฌ ๋๋น +44% ๋ฏธ๋ง์ด๋ฉด '์ ์๋ฆฌ=๋๋ธ๋์ฐ'. 0=OFF | |
| # (2026-08: 42%โ44%. 006260 2025-05-15 ํผํฌ๊ฐญ +43.3%๋ฅผ ๋๋ธ๋์ฐ์ผ๋ก ์ธ์ . ์ค์ '์์ฃผ ํ ๋ถ๊ดด'๋ | |
| # 005930'23=+45.4%, 078520'17=+46.1%๋ก 45%โ๋ผ ๊ทธ๋๋ก ์ฌ๋ฒ ์ด์ค ์ ์ง. 44%๊ฐ ๊ทธ ์ฌ์ด ๊น๋ํ ์ปท) | |
| DOUBLE_MTN_GAP_MIN = -0.10 # [์ผ๋ด] ๋๋ธ๋์ฐ ํํ: ํผํฌ๊ฐ ์ง์ ๋๋น -10%๋ณด๋ค ๋ ๋ด๋ ค๊ฐ๋ฉด '์ ์๋ฆฌ'๊ฐ ์๋๋ผ | |
| # ๊ณ๋จ์ ํ๋ฝ(์ถ์ธ๋ถ๊ดด) โ ๋๋ธ๋์ฐ ์๋. ๊น์๋๋ฆผโ์๋์ฐ์ผ๋ก ๋๊น | |
| DOUBLE_MTN_END_TOL = 0.0 # [์ผ๋ด] ๋๋ธ๋์ฐ ๋์ง์ ์กฐ๊ฑด: ๋์ง์ (2) 60์ >= ๋์ง์ (1) 60์ ร (1-TOL). | |
| # ๋์ง์ ์ด ๊ณ์ ๋ฎ์์ง๋ฉด ๋๋ธ๋์ฐ ์๋(์ง์ง ๋์ง์ ์ ํฅํด ๊ฐ๋ ์ค) | |
| DOUBLE_MTN_PCT = 0.02 # [์ผ๋ด] ์์๋ ์ค 60์ ์ด 240์ -2% ์๋๋ก ๋ด๋ ค๊ฐ์ผ '๋๋ธ๋์ฐ'(๋์ง์ 2) ์ธ์ . ์ด์ง ์ค์นจ์ ๋ฌด์ | |
| WAVE_UP_DAYS = 10 # [์ผ๋ด] '์' ์ธ์ : 60์ ์๋ก +5% ๋๊ณ N์ผ ์ฐ์ ์์ ์์ด์ผ (ํ๋ฃจ ๊ธ๋ฑ ๋ฌด์) | |
| WAVE_DEEP_PCT = 0.01 # [์ผ๋ด] 2๋ฒ์งธ ์์๋(์ฌ์ง์ )๋ ์ข ๊ฐ๊ฐ 60์ -1% ์ด์ ๊น๊ฒ ๋ด๋ ค๊ฐ์ผ ์ธ์ (7/8 ๊ฐ์ ์ด์ง ์ค์นจ ๋ฐฉ์ง) | |
| # ์ฒซ ์์๋๋ ์์๋ ์ธ์ . ํ์ ๋๋ฉด ๊ทธ ์๋๊ตฌ๊ฐ ์ฒซ๋ ๋ถํฐ ์๊ธ ๋งค์ | |
| MAX_MTN_WAVE = 2 # [์ผ๋ด] ์์๋ ๋งค์๋ ๋์ฐ N๊ฐ๊น์ง (์ฒซ ๋์ฐ + ๋๋ธ๋์ฐ = 2). ํธ๋ฆฌํ(3๋ฒ์งธ)+ ๋ถํฐ๋ ๋งค์ ์ ํจ(๊ด์ฐฐ๋ง). | |
| # ์ฌ๋ฒ ์ด์ค(๊น์๋๋ฆผโ์๋์ฐ)๋ก ๊ตฌ์กฐ๊ฐ ์๋ก ์์ํ๋ฉด ์นด์ดํธ ๋ฆฌ์ . | |
| REBASE_DEEP_PCT = 0.25 # [์ผ๋ด] โ ์์โ์ฌ๋ฒ ์ด์ค: ์์๋์ธ ์ค ์๊ณ ์๋๋ฐ 240์ฌ(์ฌ์ดํด ์์ฃผ)๋ก ๋ชป ๊ฐ๊ณ | |
| # ๋์ง์ (2)์ด 240์ ๋๋น ์ด๋ณด๋ค ๋ ๊น์ด(์: -25%) ๋ถ๊ดดํ๋ฉด, '์์๋ด์ฐ๋ฆฌโ์์๋ ์ฌ๊ฐ' ๋์ | |
| # ์ฌ๋ฒ ์ด์ค(๋์ฐ ๊ทธ๋ฆฌ๊ธฐ๋ถํฐ ๋ค์)๋ก ๋ณด๋. ํผํฌ๊ฐ ๊ณ๋จ์ํ๋ฝ(์ ์๋ฆฌ ์๋)์ธ ๊ฒฝ์ฐ์๋ง ์ ์ฉ. | |
| # 0=๋(๊ธฐ์กด: ๊น์ด๋ ์์๋ ์ฌ๊ฐ). ํ์ด๋์ค'23(-16.3%)์ ์ฌ๊ฐ๋ก ๋จ๊ธฐ๋ ค๋ฉด ์ด๋ณด๋ค ์๊ฒ. | |
| REBASE_EARLY = True # [์ผ๋ด] โ ์กฐ๊ธฐ ํ์ : ์ ์ฌ๋ฒ ์ด์ค๋ฅผ 'DIP ํ์ถ(60์ 240 ๋ณต๊ท)'๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ๋ง๊ณ , | |
| # DIP ์ถ์ ์ค์ ๊น์ด๊ฐ -REBASE_DEEP_PCT๋ฅผ ์ฐ๋ ์๊ฐ ๋ฐ๋ก ์ฌ๋ฒ ์ด์ค. | |
| # (์๋ชจ๋ 2018-08 DIP์ง์ โ 2019-12 ํ์ถ๊น์ง 16๊ฐ์์ 'ํ์ ๋๊ธฐ'๋ก ๋๋ ๋ฌธ์ ํด๊ฒฐ) | |
| # ์์ ์ฅ์น: ๋๋ธ๋์ฐ ๊ฐ๋ฅ์ฑ์ด ๋จ์์์ผ๋ฉด(ํผํฌ๊ฐญ์ด ๋ฐด๋ ์ + ๋์ง์ ์ ์ง) ์กฐ๊ธฐ๋ฐ๋ ์ ํจ. | |
| # ํผํฌ๊ฐญ์ DIP ์ง์ ์ ํ์ , ๋์ง์ ๊ฐญ์ ๋จ์กฐ๊ฐ์(ํ๋ฒ ์คํจ=์๊ตฌ์คํจ)๋ผ ์กฐ๊ธฐํ์ ์ด ์์ . | |
| # False๋ฉด ์์ ์ฒ๋ผ DIP ํ์ถ ๋๋ง ํ์ . | |
| REBASE_DIP_DAYS = 600 # [์ผ๋ด] โ ๊ธฐ๊ฐ ๊ธฐ๋ฐ ์ฌ๋ฒ ์ด์ค: DIP(๋์ง์ 2 ์ถ์ =๋งค์์ค๋จ)๊ฐ ์ด ์ผ์๋ฅผ ๋๊ฒ ์ง์๋๋ฉด | |
| # ๊น์ด(60์ gap)์ ๋ฌด๊ดํ๊ฒ ์ฌ๋ฒ ์ด์ค. '2๋ ์ง๋ฆฌ ๋๋ฆผ'์ ๋๋ฆผ์ด ์๋๋ผ ๊ตฌ์กฐ ๋ถ๊ดด ํ ์ฌ๊ฑด. | |
| # ํ์ํ ์ด์ (236200): 2020-10-06~2022-10-07 DIP๊ฐ 731์ผ. ์ฃผ๊ฐ๋ -58.4% ๋น ์ก๋๋ฐ | |
| # 60์ vs 240์ gap์ ์ต์ -11.5%๋ฟ์ด๋ผ REBASE_DEEP_PCT(-25%)์ ์ ๊ฑธ๋ฆผ. | |
| # โ ์ฒ์ฒํ ์ค๋ ํ๋ฌ๋ด๋ฆฌ๋ฉด 240์ ๋ ๊ฐ์ด ๋ด๋ ค๊ฐ ๋ ์ ๊ฐ๊ฒฉ์ด ์ ๋ฒ์ด์ง๋ ๋งน์ . | |
| # ๊ฒ์ฆ: ์์๋์ฌ๊ฐ๋ก ๋๋ DIP๋ ์ต๋ 490์ผ์ธ๋ฐ 236200๋ง 731์ผ โ ์ฌ์ด๊ฐ ํ ๋น. | |
| # 232140 ๋๋ธ๋์ฐ(577์ผ)ยทํ์ด๋์ค'23 ์ฌ๊ฐ(359์ผ)๋ 600์ผ ๋ฏธ๋ง์ด๋ผ ์์ . | |
| # ์์ ์ฅ์น๋ REBASE_EARLY์ ๋์ผ(๋๋ธ๋์ฐ ๊ฐ๋ฅ์ฑ ์์ผ๋ฉด ๋ฐ๋ ์ ํจ). 0=๋. | |
| DOUBLE_MTN_LIFELINE_FLOOR = True # [์ผ๋ด] โ ๋๋ธ๋์ฐ ์ฌ์ ์(์ฌ์ฉ์, 030530): | |
| # ๋์ง์ (2)์ด ๋์ง์ (1)๋ณด๋ค ๋ฎ์์ ธ๋, DIP ๊ตฌ๊ฐ '์ ๊ฐ'๊ฐ ์ฒซ ๋์ฐ ์๋ช ์ ์์ ์์ผ๋ฉด | |
| # ์ํ์ค๊ฐ ์ ์ฃฝ์ ๊ฒ โ ๋๋ธ๋์ฐ์ผ๋ก ์ธ์ (์๋ช ์ ยท์ํ์ค ์ ์ง, ์ฌ๋ฒ ์ด์ค ์ ํจ). | |
| # ํผํฌ๊ฐญ ๋ฐด๋(-10%~+42%) ๋ฐ(๊ณ๋จ์ํ๋ฝ/ํผํฌ์ ์ง)์ ์ฌ์ ํ ์ฌ๋ฒ ์ด์ค โ ๊ธฐ์กด ์ฐจ๋จ ์ ์ง. | |
| # โป 600์ผ ์ฅ๊ธฐDIP ์ฌ๋ฒ ์ด์ค๋ ์ด ๊ท์น๋ณด๋ค ์ฐ์ (236200: ์๊ฐ์ด ๊ตฌ์กฐ๋ฅผ ์ด๊น). | |
| # False๋ฉด ๊ธฐ์กด(๋์ง์ (2)โฅ๋์ง์ (1) ์กฐ๊ฑด๋ง). | |
| WAVE_CONFIRM_GAP = 0.07 # [์ผ๋ด] โ ์์๋ ๋ณต๊ท ํ์ธ: ๋์ฐ์ฑ๊ณต/๋๋ธ๋์ฐ/์ฌ๊ฐ๋ก ์์๋๊ฐ ์ด๋ ค๋, | |
| # 60์ ์ด 240์ ์๋ก ์ด๋งํผ(+7%) ์ฌ๋ผ๊ฐ์ผ ์ค์ ๋งค์ ์์. | |
| # 60์ ์ด 240์ ์ ์ฐ๋ ๋ซ๊ณ 24~70์ผ ๋ง์ ๋๊บพ์ด๋ '๊ฐ์ง ๋ณต๊ท'(๋ชจ์ํํ)์์ | |
| # ๋ฌผ๋ฆฌ๋ ๊ฒ ๋ฐฉ์ง. ๊ฒ์ฆ: ๊ฐ์ง 9๊ฑด ์ต๋ 5.7% vs ์ง์ง 21๊ฑด ์ต์ 9.5% โ ์ฌ์ด๊ฐ ๋น์ด 100% ๋ถ๋ฆฌ. | |
| # ๋๊ฐ: ์ง์ง๋ ์ง์ ์ด 6~141์ผ ๋ฆ์ด์ง(๋๋ถ๋ถ 12~16์ผ). 0 = ์ด ํํฐ ๋(์์ ๋์). | |
| WAVE_CONFIRM_DAYS = 90 # [์ผ๋ด] โ ์๊ฐ ๊ธฐ๋ฐ ๋ณต๊ท ํ์ธ (WAVE_CONFIRM_GAP๊ณผ OR): | |
| # gap์ด +7%์ ๋ชป ๋ฏธ์ณ๋, 60์ ์ด 240์ ์(gap>0)์์ ์ด ์ผ์๋งํผ '์ฐ์ ์์กด'ํ๋ฉด | |
| # ์ง์ง ๋ณต๊ท๋ก ์ธ์ โ ์์๋ ๋งค์ ์์. ์ค๊ฐ์ gapโค0(240 ์๋๋ก)๋๋ฉด ์๊ฐ์นด์ดํฐ ๋ฆฌ์ (๊ฐ์ง). | |
| # ๊ทผ๊ฑฐ: ๊ฐ์ง๋ชจ์๋ 240 ์ ์์กด ์ต๋ 85์ผ(000660'08=34, 090430'17=85)์ ๋๊บพ์ด๋๋ฐ, | |
| # ์ง์ง(์ผ์ฑ'19)๋ gap์ด ๋๋ด +7% ๋ฏธ๋ฌ(์ต๋+1.3%)์ด์ด๋ 240 ์ 399์ผ ์์กด โ ์๊ฐ์ด ํ๋ณ. | |
| # 0 = ์๊ฐ ํ์ธ ๋(gap๋ง์ผ๋ก ํ์ธ). | |
| MINBAR_STATE = False # [๋ถ๋ด] โ ๋ถ๋ด(5/15/60๋ถ)๋ ์ผ๋ด์ ์ํ๊ฐ์ง ์ฌ์ฉ (2026-08): | |
| # โ ๏ธ True๋ก ์ผ๋ฉด ๋ถ๋ด์ด ์ผ๋ด DIP '๋จ๊ณ'์ ๋ค์ด๊ฐ ๋งค์๊ตฌ์กฐ๊ฐ ํ ๋ฐ๋(๋งค์ 4~8๋ฐฐ ํญ์ฆ). | |
| # John์ "ํฐ์น๋งค์ ๊ทธ๋๋ก"๋ฅผ ์ํ๋ฏ๋ก ์ด ๋ฐฉ์(๋จ๊ณ ์ฌ๋ผ์ฐํ )์ ๋ถ์ ํฉ โ ๊ธฐ๋ณธ OFF. | |
| # ์ํํ์๋ '๋จ๊ณ ์ฌ๋ผ์ฐํ ' ๋ง๊ณ '๋ณ๋ ฌ ์ค๋ฒ๋ ์ด'๋ก ํด์ผ ํจ(๋งค์ ๋ถ๋ณ). ์๋๋ ์ ์ค๋ช : | |
| # DIP(๋งค์๋ฉ์ถค)ยท๋ณต๊ทํ์ธยท์ค์นจยท๋ชจ์๋ฅผ ๋ถ๋ด์๋ ์ ์ฉ. ์๊ฐ๊ธฐ์ค(์ผ)์ '๋ด ๊ฐ์'๋ก ํ์ฐ. | |
| # ๋จ ๋งค์ผ๋งค์ยท์๊ธยท๊ฐ์์๋ช ์ ์ ๋ถ๋ด์ ์ ์(ํฐ์น๊ธฐ๋ฐ ๋งค์ ๋ฐฉ์์ ๊ทธ๋๋ก). | |
| # False๋ฉด ์์ ์ฒ๋ผ ๋ถ๋ด์ ์์ ํฐ์น๊ธฐ๋ฐ(์ํ๊ฐ์ง ์์). | |
| WAVE_TOUCH_BUY = True # [์ผ๋ด] โ ์ค์นจ ๊ตฌ๊ฐ ๋งค์ ํ์ฉ (WAVE_CONFIRM_GAP๊ณผ ์ง): | |
| # 60์ ์ด 240์ ์ '์ค์น๊ฑฐ๋ ๊ทธ ์๋'(gap โค 0, DIP -2% ์ง์ ์ )์ ์์ผ๋ฉด | |
| # ๋ชจ์(๊ฐ์ง ๋ณต๊ท)๊ฐ ์์ ์ ์๊ธด ๊ฒ = ๋ฐ๋ฅ ๋ค์ง๋ ์ค โ ์์๋ ๋งค์ ํ์ฉ. | |
| # ๊ฒฐ๊ณผ: ๋งค์ ๊ธ์ง ๊ตฌ๊ฐ์ gap์ด (0, +7%) ์ด๋ฆฐ ๋ฐด๋๋ฟ. ๊ทธ ์(์ง์ง ๋ณต๊ท)ยท์๋(์ค์นจ) ๋ค ๋งค์. | |
| # False๋ฉด ์์ ์ฒ๋ผ 60>240 + ํ์ธ(+7%) ๋ผ์ผ๋ง ๋งค์. | |
| WAVE_HAT_ONLY_FRESH = True # [์ผ๋ด] โ ๋ชจ์ ์ฒดํฌ๋ฅผ '๋์ฐ ์์ฑ ์งํ ๋ณต๊ท'์๋ง ์ ์ฉ (์ฌ์ฉ์ ์ง์ 2026-08): | |
| # ๋ชจ์๋ฅผ ์์ฌํ ์ํฉ์ '๋ง์ด ์ค๋ฅธ ๋ค ๊บพ์ด๋ ์๊ฐ' = ๋์ฐ์ ํฌ๊ฒ ๊ทธ๋ฆฌ๊ณ 60์ ์ด 240์ ์๋ก | |
| # ๋ณต๊ทํ๋ ๊ทธ ์๊ฐ. ์ด๋ฏธ ์์๋ ์ํ์ค๊ฐ ๋์๊ฐ๋ ์ค(๋๋ธ๋์ฐ ๋ฆฌ์ / ์์๋ ์ฌ๊ฐยท๊ณ์)์๋ | |
| # ๋ชจ์๊ฐ ์๊ธฐ๋ ๋ง๋ ๋ฌด์ํ๊ณ ์ค์นจ ๋งค์ ํ์ฉ. | |
| # โ ์์๋๋ฅผ ์ฐ ์ฌ๊ฑด์ด '๋์ฐ์ฑ๊ณตโ์์๋์์'์ผ ๋๋ง ๋ชจ์ ์ฒดํฌ. False๋ฉด ํญ์ ์ฒดํฌ. | |
| WAVE_HAT_PCT = 0.0103 # [์ผ๋ด] โ ์ค์นจ vs ๋ชจ์ ๊ตฌ๋ถ (WAVE_TOUCH_BUY ๋ณด์ ): | |
| # '์ค์นจ(๋ฐ๋ฅ ๋ค์ง๊ธฐ)'์ ์์ง ํ ๋ฒ๋ ์ ์ฌ๋ผ๊ฐ ์ง์ง ๋ฐ๋ฅ์ด์ด์ผ ํจ. ๊ทธ๋ฐ๋ฐ ์ด ์์๋์์ | |
| # ์ด๋ฏธ 60์ ์ด 240์ ์๋ก ์ด๋งํผ(+1%) ์ฌ๋ผ๊ฐ '๋ชจ์ ๋ด์ฐ๋ฆฌ'๋ฅผ ๋ง๋ ๋ค ๋ค์ gapโค0๋ก | |
| # ๋ด๋ ค์ค๋ฉด, ๊ทธ๊ฑด ๋ฐ๋ฅ์ด ์๋๋ผ '๋ชจ์์ ํ๊ฐ ๋ค๋ฆฌ' โ ์ค์นจ ๋งค์ ๊ธ์ง(ํ์ธ ๋๊ธฐ ์ ์ง). | |
| # ๊ทผ๊ฑฐ(166090): 2019-04~05 gap์ด +1.5%๊น์ง ์ฌ๋๋ค๊ฐ(๋ชจ์) 5/22 gap-0.0%๋ก ๋ด๋ ค์ค์ | |
| # ๊ธฐ์กด ์ฝ๋๊ฐ '์ค์นจ=๋ฐ๋ฅ'์ผ๋ก ์คํํด ๋งค์. ๋ด์ฐ๋ฆฌ๋ฅผ ๋ง๋ ์ด๋ ฅ์ ๋ด์ผ ์ง์ง ๋ฐ๋ฅ๊ณผ ๊ตฌ๋ถ๋จ. | |
| # 0 = ์ด ๋ณด์ ๋(์์ ์ฒ๋ผ gapโค0์ด๋ฉด ๋ฌด์กฐ๊ฑด ์ค์นจ ๋งค์). | |
| # โป ์๊ณ 1.03%์ธ ์ด์ : 090430 2008 ๋ชจ์ ์ค์ธก 1.0208%๋ฅผ '๋ชจ์ ์๋'์ผ๋ก ๋๋ผ๋ ์ฌ์ฉ์ ์ง์ | |
| # ("1.02% ์ดํ๋ ๋ชจ์ ์๋, ์ด๊ณผ๊ฐ ๋ชจ์"). ๋ฐ๋ก ์ ์ผ์ด์ค๋ 030530 1.0445%, 112610 1.1473%. | |
| WAVE_BUY_FLOOR = 0.08 # [์ผ๋ด] ์์๋ ๋งค์ ํํ: ์ข ๊ฐ๊ฐ 240์ ๋ณด๋ค -8% ๋๊ฒ ๊น์ผ๋ฉด '๊น์ ๊ตฌ๊ฐ'. | |
| # ๊น์ ๊ตฌ๊ฐ์์ ํ๋ฝ ์ค์ ๋งค์ ๋ณด๋ฅ('์์๋ ์์๋ค'), ๋ฐ๋ฑํ ๋ (์ข ๊ฐ>์ ์ผ์ข ๊ฐ)์๋ง ๋งค์. | |
| # โ 2008ํ ๊ณ์ํ๋ฝ=์นผ ํํผ / 2020ํ V๋ฐ๋ฅ=๋ฐ๋ฑ์๊ฐ ์ฌ๊ฐ. 0์ด๋ฉด ํํฐ ๋. | |
| EARLY_EXTEND = True # [์ผ๋ด] ์์ฅ์ด๊ธฐ 240์ ๋ค๋ก์ฐ์ฅ ON/OFF. ๊ธฐ๋ณธ ON(ํํ์ฐ์ฅ ์ฑํ). OFF๋ฉด ๊ธฐ์กด(i<240 ์คํต)๊ณผ ๋์ผ | |
| EXTEND_MODE = 'flat' # 'flat'=์ฒซ ์ง์ง240๊ฐ ์ํ ์ฐ์ฅ(๊ฒ์ฆ์ ์ด๊ธฐ๋์ฐ ํฌ์ฐฉ๋จ) / 'slope'=๊ธฐ์ธ๊ธฐ์ฐ์ฅ(์ด๊ธฐ born-above๋ผ ๋ฏธํฌ์ฐฉ) | |
| EXTEND_SLOPE_WIN = 60 # slope ๋ชจ๋ ์ ์ฉ: ๊ธฐ์ธ๊ธฐ = ์ฒซ ์ง์ง240 ์ดํ N์ผ ํ๊ท ์ผ๋ณํ | |
| CYCLE_END = 0.10 # 60์ ์ด 240์ -10% ์๋ โ ์ฌ์ดํด ์ข ๋ฃ (์ง์ง ์ถ์ธ๋ถ๊ดด. -5%๋ ๋๋ฌด ๋นก๋นกํด์ ๊น์ ๋๋ฆผ๋ ๋๊น) | |
| SELL_ORPHAN_ON_CYCLEEND = True # [์ฑํ 2026-08] โ ์ฌ์ดํด์ข ๋ฃ๋ก hold=False ๋ ๋ค์๋, ํ ์ ์๋ ๋ฌผ๋(held)์ด | |
| # ๋จ์์๊ณ ์ ๊ฐ๊ฐ ์๋ช ์ ๋ฐ์ผ๋ก ๊ฐ๋ฉด ๊ทธ ๋ฌผ๋ ์ ๋๋งค๋. | |
| # ๋ฌธ์ : ์ฌ์ดํด์ข ๋ฃ๊ฐ hold๋ฅผ ๊บผ๋ฒ๋ ค ๊ทธ ๋ฌผ๋์ด ๋ณดํธ ์์ด ๋ฐฉ์น๋๋ ๋นํ์ด ์์์ | |
| # (006260 5๋ถ๋ด: ์ฌ์ดํด1 60๋ง์์ด ์๋ช ์ 439,368 ๊นจ์ ธ๋ ์ ํ๋ฆผ โ ๋ฐฉ์น). | |
| # โป ์ ์ฉ ๋ฒ์๋ SELL_ORPHAN_TFS ๋ก ์ ํ(ํ์ฌ 5๋ถ/15๋ถ๋ด๋ง). ์ผ๋ด์ ์ตํ๋ณด๋ฃจ๊ฐ ๋ด๋นํ๋ฏ๋ก ์ ์ธ. | |
| SELL_ORPHAN_TFS = ('5min','15min') # โ ์ ๊ท์น์ ์ ์ฉํ ํ์ํ๋ ์. ์ผ๋ด/1์๊ฐ๋ด ์ ์ธ(์ํ๋ฉด ์ฌ๊ธฐ์ ์ถ๊ฐ). | |
| SELL_COOLDOWN_DAYS = 90 # ์ ๋๋งค๋(์ตํ๋ณด๋ฃจ ๋ฑ) ํ ์ด ๊ธฐ๊ฐ ๋งค์ ๊ธ์ง (30โ90์ผ๋ก ํ๋) | |
| WAVE_OVERRIDE_COOLDOWN = True # [์ผ๋ด] โ ์ผ๋ด 1ยท2๋ฒ์งธ ์์๋ ๋งค์๋ 90์ผ ์ฟจ๋ค์ด๋ณด๋ค ์ต์ฐ์ โ ์ฟจ๋ค์ด ๋ฌด์ํ๊ณ ๋งค์. | |
| # (๋ถ๋ด 5ยท15ยท60๋ถยท๊ธฐํ ๋งค์๋ ๊ทธ๋๋ก 90์ผ ๊ธ์ง ์ ์ง). False๋ฉด ์ผ๋ด๋ ์ฟจ๋ค์ด ์ ์ฉ. | |
| VCP_MULT = 3 # VCP ์ด์์๋ ๊ตฌ๊ฐ์์ ์์๋ ๋งค์ ์ ๋ฐฐ์ | |
| AMT = {'5min': {'a': 200_000, 'b': 300_000}, | |
| '15min': {'a': 300_000, 'b': 500_000}, | |
| '1hour': {'a': 800_000, 'b': 1_200_000}, | |
| # ์ผ๋ด: ํฐ์น 1ํ ๋งค์๊ฐ ์๋๋ผ '60์ ์๋ ์๋ ๋์ ๋งค์ผ ๋ฐ๋ณต ๋งค์' | |
| '1day': {'daily1': 1_000_000, 'daily2': 500_000}} | |
| def _daily_peaks(df): | |
| """์ผ๋ด ํผํฌ ์ฌ์ ๊ณ์ฐ (peak_gap_debug์ ๋์ผ ๋ก์ง). | |
| 60์ ์ด 240์ ์์ ์๋ ๊ฐ ๊ตฌ๊ฐ์ 60์ ์ต๊ณ ์ . ๋ฐํ [(ํผํฌ์ผ์str, ํผํฌ60์ ๊ฐ)]""" | |
| d = df[(df['Close'] > 0)].copy() | |
| c = d['Close'] | |
| m60 = c.rolling(MA_60, min_periods=20).mean() | |
| m240 = c.rolling(MA_240, min_periods=60).mean() | |
| ok = m60.notna() & m240.notna() | |
| above = (m60 > m240) & ok | |
| grp = (above != above.shift()).cumsum() | |
| peaks = [] | |
| for g, idx in d.index.to_series().groupby(grp): | |
| seg = idx.index | |
| if not above.loc[seg[0]]: | |
| continue | |
| if len(seg) < 5: | |
| continue | |
| seg_m60 = m60.loc[seg] | |
| pk_i = seg_m60.idxmax() | |
| pk_gap = m60.loc[pk_i] / m240.loc[pk_i] - 1 | |
| if pk_gap < PEAK_MIN_PCT['1day']: | |
| continue | |
| peaks.append((str(pk_i)[:10], float(m60.loc[pk_i]))) | |
| return peaks | |
| LAST_STATUS = '' # be.run ํ '์ง๊ธ ์ํ' ํ ์ค ์์ฝ (full_debug/app์ด ์ฝ์) | |
| LAST_STATUS_KIND = 'wait' # ์ํ ์ข ๋ฅ(์์น ์ฉ): buy/danger/dip/done/wait | |
| LAST_STATUS_SHORT = '' # ์งง์ ์ํ๋ช (๋ผ๋ฒจ์ฉ): '์ฐ๋ด์ฐ๋ฆฌ ๊ทธ๋ฆฌ๋ ์ค' ๋ฑ | |
| LAST_STATUS_DETAIL = '' # ์ํ ์์ธ(๋ท๋ถ๋ถ): '์์ ๋ฐ๋ฅ ... / ์๋ช ์ ...' | |
| def run(df, tf='5min', sf=None, sell_dates=None, vcp_wins=None, block_ranges=None): | |
| # sell_dates: ์ ๋๋งค๋ ๋ฐ์์ผ ๋ฆฌ์คํธ. ์ดํ SELL_COOLDOWN_DAYS ๋งค์๊ธ์ง | |
| # block_ranges: [(start,end)] ์ด ๊ตฌ๊ฐ์ ๋งค์ ๊ธ์ง (๋ถ๋ด์ฉ โ ์ผ๋ด์ด DIP(๋งค์์ค๋จ)์ด๋ฉด ๋ถ๋ด๋ ๋ฉ์ถค) | |
| # vcp_wins: VCP ์ ํจ๊ตฌ๊ฐ [{'start','end'}]. ์ด ๊ตฌ๊ฐ์ ์์๋ ๋งค์๋ VCP_MULT(3๋ฐฐ) | |
| daily = (tf == '1day') # ์ผ๋ด = ๋งค์ผ ๋ฐ๋ณต๋งค์ ๋ชจ๋ | |
| if daily: | |
| d1 = AMT['1day']['daily1']; d2 = AMT['1day']['daily2'] | |
| a = b = 0 | |
| else: | |
| a = AMT[tf]['a']; b = AMT[tf]['b'] | |
| recov = RECOVER_PCT[tf]; wrise = WAVE_RISE_PCT[tf] | |
| day_peaks = _daily_peaks(df) if tf=='1day' else [] | |
| def _peak_before(ds): | |
| """ds(YYYY-MM-DD) ์ด์ ์ ๋ง์ง๋ง ํผํฌ๊ฐ. ์์ผ๋ฉด None""" | |
| out=None | |
| for pdt,pv in day_peaks: | |
| if pdt < ds: out=pv | |
| else: break | |
| return out | |
| pmin = PEAK_MIN_PCT[tf]; emin = ENDLOW_MIN_PCT[tf] | |
| # ๋ฐ์ดํฐ ํด๋ฆฌ๋: 0/์์ ๊ฐ๊ฒฉ ๋ด ์ ๊ฑฐ (์ก๋ฉด๋ถํ ๋ฑ ์ํฐํฉํธ) | |
| df = df[(df['Close']>0) & (df['Low']>0) & (df['High']>0)].copy() | |
| # ์ ๋๋งค๋ ํ ์ฟจ๋ค์ด(๋งค์๊ธ์ง) ํ์ | |
| _sd = sorted(pd.to_datetime(sell_dates)) if sell_dates else [] | |
| _br = [(pd.Timestamp(a), (pd.Timestamp(b) if b is not None else None)) for a,b in (block_ranges or [])] | |
| def _blocked(ts): | |
| # ๋งํ์ผ๋ฉด ์ฌ์ ๋ฌธ์์ด ๋ฐํ(์ด๋ ์ ๋๋งค๋/DIP ๋๋ฌธ์ธ์ง), ์ ๋งํ์ผ๋ฉด None | |
| for sd in _sd: | |
| if sd <= ts <= sd + pd.Timedelta(days=SELL_COOLDOWN_DAYS): | |
| return f"{str(sd)[:10]} ์ ๋๋งค๋ ํ {SELL_COOLDOWN_DAYS}์ผ ๋งค์๊ธ์ง" | |
| for a,b in _br: # ์ผ๋ด DIP(๋งค์์ค๋จ) ๊ตฌ๊ฐ โ ๋ถ๋ด๋ ๊ธ์ง | |
| if ts >= a and (b is None or ts < b): | |
| return f"์ผ๋ด {str(a)[:10]}๋ถํฐ ๋งค์๋ฉ์ถค(๋๋ฆผ ํ์ ) ๊ตฌ๊ฐ์ด๋ผ ๊ธ์ง" | |
| return None | |
| c = df['Close']; lo = df['Low'] | |
| ma60 = c.rolling(MA_60, min_periods=20).mean() | |
| bbl = c.rolling(BB_WIN).mean() - BB_STD * c.rolling(BB_WIN).std() | |
| # โโ 240์ : ์ง์ง๊ฐ + ์์ฅ์ด๊ธฐ ์ ํ ๋ค๋ก์ฐ์ฅ(๊ธฐ์ธ๊ธฐ ๋ณด์กด) โโ | |
| # 240๋ด์ด ์ ์์ธ ๊ทน์ด๊ธฐ๋ ์ฒซ ์ง์ง240๊ฐ์์ ์์๊ธฐ์ธ๊ธฐ๋ก ๊ณผ๊ฑฐ๋ก ์ธ์ฝ โ ์ ์ ์ผ์ชฝ์ผ๋ก ๋๋ ค ๊ทธ๋ฆผ | |
| ma240_true = c.rolling(MA_240).mean() # min_periods=240 (์ง์ง) | |
| ma240 = ma240_true.copy() | |
| if EARLY_EXTEND and ma240_true.notna().any(): | |
| i0 = int(ma240_true.notna().values.argmax()) # ์ฒซ ์ง์ง240 ๋ฐ | |
| v0 = float(ma240_true.iloc[i0]) | |
| slope = 0.0 if EXTEND_MODE=='flat' else float(ma240_true.diff().iloc[i0:i0+EXTEND_SLOPE_WIN].mean()) | |
| vals = ma240.values.copy() | |
| for _j in range(i0): | |
| vals[_j] = v0 - slope*(i0 - _j) | |
| ma240 = pd.Series(vals, index=ma240.index) | |
| idx = df.index | |
| HI = 9e12 | |
| phase='WAIT' # WAIT โ PEAK(์ฐ๋ด์ฐ๋ฆฌ,๋์ง์ ) โ WAVE(์์๋) | |
| start_ma60=HI; start_bb60=None # ์์์ ํ๋ณดA: 60์ ์ต์ + ๊ทธ BB | |
| start_pxlow=HI; start_bbpx=None # ์์์ ํ๋ณดB: ๊ฐ๊ฒฉ ์ต์ + ๊ทธ BB | |
| base_bbmin=HI # ํ๋ณดC(B์): ๋ฒ ์ด์ค ๊ตฌ๊ฐ ์ ์ฒด์ ์ต์ BBํ๋จ | |
| life=None | |
| peak_gap=-9.9 | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI; endlow_bb60=None; endlow_bbpx=None; endlow_dt=None # ๋์ง์ | |
| start_dt=None; mtn_s60=HI; mtn_sdt=None | |
| prev_end60=None; peak_m60=0.0 | |
| dip_ma60=HI; dip_bb60=None; dip_px=HI; dip_bbpx=None; dip_dt=None; dip_gap=0.0; wave_peak_ma60=0.0; prev_mtn_peak=None; dip_peak=0.0 | |
| dip_start_dt=None; dip_start_i=None # DIP ์์์ผ/์์๋ด (๊ธฐ๊ฐ ํ์ : ์ผ๋ด=์ผ์, ๋ถ๋ด=๋ด์) | |
| dip_floor_logged=False # '์กฐ๊ธฐ์ฌ๋ฒ ์ด์ค ๋ณด๋ฅ(์๋ช ์ ๋ฐ๋ฅ)' ์ด๋ฒคํธ 1ํ ์ถ๋ ฅ์ฉ | |
| vlife=None; vseg_i=0 # ๊ฐ์ ์๋ช ์ / ์ด ์์์ธ๋ ๋งค์ ์์ ์ธ๋ฑ์ค(buys ๊ธฐ์ค) | |
| vbreak_dt=None # ๊ฐ์์๋ช ์ ์๋๋ก ์ฒ์ ๋ด๋ ค๊ฐ ๋ (์ ์ ํ์ ์ฉ) | |
| one_wave=False # ์ด๋ฒ ์์๋๋ 1๋ฒ์งธ๋ง ๋งค์(๊น์ ๋๋ฆผ ๋ค ์ฌ๊ฐ) | |
| dip_bbmin=HI # DIP ๊ตฌ๊ฐ ์ ์ฒด์ ์ต์ BBํ๋จ (DOUBLE_MTN_RELIFE: ์ ์๋ช ์ ํ๋ณด) | |
| wave_confirmed=False; conf_logged=False; touch_logged=False # ์์๋ ๋ณต๊ท ํ์ธ ์ฌ๋ถ(๊ฐ์ง ๋ณต๊ท ์ฐจ๋จ) | |
| cross_up_dt=None # ์ด๋ฒ ์์๋์์ 60์ ์ด 240์ ์(gap>0)๋ก ์ฒ์ ์ฌ๋ผ๊ฐ ๋ (์๊ฐ ๊ธฐ๋ฐ ํ์ธ์ฉ) | |
| wave_hat=0.0 # ์ด๋ฒ ์์๋์์ 60์ ์ด 240์ ์๋ก ๋ง๋ ์ต๋ gap (๋ชจ์ ๋ด์ฐ๋ฆฌ ๊ฐ์ง โ ์ค์นจ ์คํ ๋ฐฉ์ง) | |
| wave_fresh=True # ์ด ์์๋๋ฅผ ์ฐ ์ฌ๊ฑด์ด '๋์ฐ์ฑ๊ณต'์ธ๊ฐ(=๋ง์ด ์ค๋ฅธ ๋ค ๋ณต๊ท) โ ๋ชจ์ ์ฒดํฌ ๋์ ์ฌ๋ถ | |
| life_broken=False # PEAK ์ค ์๋ช ์ ์ดํํ๋์ง (LIFELINE_KEEP_MTN: ์์ ํ์ง๋ง ๋์ฐ์ ๊ณ์ ๊ด์ฐฐ) | |
| wave=0; armed=False; b240={}; flag240up=False; mtn_count=0 # mtn_count: ์์๋ ๋งค์ํ ๋์ฐ ์๋ฒ(์ฒซ=1, ๋๋ธ=2, ํธ๋ฆฌํ=3โฆ) | |
| hold=False; lwl=None; lwl_c=None; daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| held=False # ์ค๋ณด์ (ํ ์ ์๋ ๋ฌผ๋ ์์) โ hold(๋งค์๊ตญ๋ฉด์ค)์ ๋ณ๊ฐ. ์ค์ ์ ๋๋งค๋ ๋๋ง False | |
| prev_above=None | |
| ev=[]; buys=[]; mts=[]; blocked_n=[0] | |
| def _in_vcp(dt_): | |
| if not vcp_wins: return False | |
| ds_ = str(dt_)[:10] | |
| for w in vcp_wins: | |
| if ds_ >= w['start'] and (w['end'] is None or ds_ < w['end']): | |
| return True | |
| return False | |
| def _buy(dt_, px_, amt_, lbl_, force=False): | |
| # force=True โ ์ ๋๋งค๋ 90์ผ ์ฟจ๋ค์ด ๋ฌด์(์ผ๋ด 1ยท2๋ฒ์งธ ์์๋ ์ต์ฐ์ ). ๋ถ๋ด/๊ธฐํ๋ force=False๋ผ ์ฟจ๋ค์ด ์ ์ง. | |
| _why = None if force else _blocked(dt_) | |
| if _why: | |
| blocked_n[0]+=1 | |
| ev.append((dt_,' โ๋งค์๋ณด๋ฅ',_why)) | |
| return False | |
| if _in_vcp(dt_): # โ VCP ์ด์์๋ ๊ตฌ๊ฐ โ 3๋ฐฐ | |
| amt_ = amt_ * VCP_MULT | |
| lbl_ = lbl_ + f' ร{VCP_MULT}(VCP)' | |
| nonlocal held | |
| buys.append((dt_,px_,amt_,lbl_)); held=True; return True | |
| for i in range(len(df)): | |
| if i < (MA_60 if EARLY_EXTEND else MA_240): | |
| # โ ์์ฅ ์ด๊ธฐ(60์ ์๊ธฐ๊ธฐ ์ ) ๊ธ๋ฝ BBํ๋จ๋ ์๋ช ์ ํ๋ณด๋ก ์ถ์ . | |
| # 60์ ์ด ์์ด ์ด ๋ด๋ค์ ๊ด์ฐฐ์์ ๋น ์ง์ง๋ง, BBํ๋จ(20๋ด)์ ์ด๋ฏธ ์์ผ๋ฏ๋ก ์ต์ ๊ฐ์ ๊ธฐ์ตํด ๋ . | |
| # (166090: 2017-05-31 BBํ๋จ 7,473์ด ์ฒซ ๋์ฐ ์๋ช ์ ํ๋ณด์์ ๋น ์ง๋ ๋ฌธ์ ํด๊ฒฐ) | |
| if EARLY_EXTEND and LIFELINE_BASE_MIN: | |
| _bl0 = float(bbl.iloc[i]) | |
| if not np.isnan(_bl0) and _bl0 < base_bbmin: base_bbmin = _bl0 | |
| continue | |
| d=idx[i]; C=float(c.iloc[i]); L=float(lo.iloc[i]) | |
| m60=float(ma60.iloc[i]); m240=float(ma240.iloc[i]); bl=float(bbl.iloc[i]) | |
| if np.isnan(m60) or np.isnan(m240) or np.isnan(bl): continue | |
| above = m60 > m240 | |
| gap = m60/m240 - 1 | |
| # โ [์ธก์ ์ฉ] ๊ณ ์๋ฌผ๋ ๋งค๋: ์ฌ์ดํด์ข ๋ฃ๋ก hold=False ๋์ง๋ง ํ ์ ์๋ ๋ฌผ๋(held)์ด ๋จ์ | |
| # ์๋ช ์ ๋ฐ์ผ๋ก ๊ฐ๋ฉด ๊ทธ๋ ์ ๋๋งค๋. WAIT ๋จ๊ณ ์ ์ฉ(๋ค๋ฅธ ๋จ๊ณ๋ ์์ฒด ์๋ช ์ ์ฒดํฌ๊ฐ ์์). | |
| if SELL_ORPHAN_ON_CYCLEEND and tf in SELL_ORPHAN_TFS and held and (not hold) and phase=='WAIT' and life and L < life: | |
| ev.append((d,'์๋ช ์ ์ดํโ์ ๋๋งค๋(์ฌ์ดํด์ข ๋ฃํ ์์ฌ๋ฌผ๋)', | |
| f'์ ๊ฐ {L:,.0f} < ์๋ช ์ {life:,.0f} โ ์ฌ์ดํด ๋๋ ๋ค ๋จ์์๋ ๋ฌผ๋ ์์ ')) | |
| held=False | |
| # โ ๊ฐ์ ์๋ช ์ ์ดํ: ์ด ์์์ธ๋์์ ์ฐ ๋ฌผ๋๋ง ๋งค๋ + ์ฌ๋ฒ ์ด์ค(๋์ฐ๋ถํฐ ๋ค์) | |
| if VIRTUAL_LIFELINE and vlife and phase in ('WAVE','DIP'): | |
| if C >= vlife: | |
| vbreak_dt=None # ์ข ๊ฐ๊ฐ ์ ์๋ก ํ๋ณต โ ์ ์ ๋ฆฌ์ (์๋ ์ผ) | |
| elif L < vlife and vbreak_dt is None: | |
| vbreak_dt=d # ์ฒ์ ๊นฌ ๋ ๊ธฐ๋ก | |
| if (VIRTUAL_LIFELINE and vlife and phase in ('WAVE','DIP') and vbreak_dt is not None | |
| and (d - vbreak_dt).days >= VLIFE_GRACE_DAYS): | |
| _sq = buys[vseg_i:] | |
| _amt = sum(x[2] for x in _sq) | |
| _sh = sum(x[2]/x[1] for x in _sq) | |
| _avg = (_amt/_sh) if _sh else 0 | |
| ev.append((d,'๊ฐ์์๋ช ์ ์ดํโ๊ตฌ๊ฐ๋งค๋', | |
| f'์ ๊ฐ {L:,.0f} < ๊ฐ์์๋ช ์ {vlife:,.0f} โ ์ด ์์์ธ๋ ๋งค์๋ถ๋ง ๋งค๋(์ง์ง ์๋ช ์ {life:,.0f}๋ ์ ์ง) ' | |
| f'| ๊ตฌ๊ฐ๋งค์ {len(_sq)}ํ {_amt:,.0f}์ ํ๋จ {_avg:,.0f} | {VLIFE_GRACE_DAYS}์ผ ์ ์ ๋ด๋ด ํ๋ณต ์คํจ({str(vbreak_dt)[:10]}๋ถํฐ) โ ๋์ฐ๋ถํฐ ๋ค์')) | |
| vlife=None; vseg_i=len(buys); vbreak_dt=None | |
| mtn_s60 = m60; mtn_sdt = d | |
| life = min(bl, life if life else bl) | |
| phase='PEAK'; peak_gap=gap; peak_m60=m60; vlife=None; vbreak_dt=None; vseg_i=len(buys) # โ ์ ์ฐ ๊ทธ๋ฆฌ๊ธฐ ์์ โ ์ ๊ฐ์์๋ช ์ ํ๊ธฐ | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI | |
| hold=False; wave=0; armed=False; b240={}; flag240up=False | |
| daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| mtn_count=0; life_broken=False | |
| wave_confirmed=False; conf_logged=False; touch_logged=False; cross_up_dt=None; wave_hat=0.0 | |
| continue | |
| if phase=='WAIT': | |
| # โ ๋ฒ ์ด์ค ๊ตฌ๊ฐ ์ ์ฒด์ ์ต์ BBํ๋จ ์ถ์ (above/below ๋ฌด๊ด โ ํํ์ฐ์ฅ ๊ตฌ๊ฐ ๋์) | |
| if bl < base_bbmin: base_bbmin = bl | |
| # 60์ ์ด 240 ์๋ โ '60์ ์ต์ ์ ' ์ถ์ (๊ฐ๊ฒฉ ๊ธ๋ฝ๋ด ์๋) | |
| if not above: | |
| if m60 < start_ma60: # 60์ ์ต์ ์ | |
| start_ma60=m60; start_bb60=bl; start_dt=d | |
| if L < start_pxlow: # ๊ฐ๊ฒฉ ์ต์ ์ | |
| start_pxlow=L; start_bbpx=bl | |
| # 60์ ์ด 240 ์๋ก ํฌ๋ก์ค โ ์ฐ๋ด์ฐ๋ฆฌ ์์ | |
| if prev_above is False and above and start_bb60 is not None: | |
| bb_low = min(start_bb60, start_bbpx) # โ ๋ ์ค ๋ ๋ฎ์ BBํ๋จ (์คํ์ดํฌ ํํ๊ฐ ์๋ ํํผ) | |
| if LIFELINE_BASE_MIN and base_bbmin < HI: | |
| bb_low = min(bb_low, base_bbmin) # โ B์: ๋ฒ ์ด์ค ์ ์ฒด ์ต์ BBํ๋จ๊น์ง ๊ณ ๋ ค | |
| life=bb_low*(1-LIFELINE_MARGIN) | |
| base_bbmin=HI # ์ ๋์ฐ ์์ โ ๋ฒ ์ด์ค ์ถ์ ๋ฆฌ์ | |
| mtn_s60=start_ma60; mtn_sdt=start_dt # ์ด ๋์ฐ์ ์์์ ๊ณ ์ | |
| life_broken=False | |
| phase='PEAK'; peak_gap=gap; peak_m60=m60; vlife=None; vbreak_dt=None; vseg_i=len(buys) # โ ์ ์ฐ ๊ทธ๋ฆฌ๊ธฐ ์์ โ ์ ๊ฐ์์๋ช ์ ํ๊ธฐ | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI | |
| ev.append((d,'์ฐ๋ด์ฐ๋ฆฌ์์',f'๋์ฐ ์์์ {str(mtn_sdt)[:10]} (60์ {mtn_s60:,.0f}) / ์๋ช ์ {life:,.0f}')) | |
| elif phase=='PEAK': | |
| if peak_gap < gap: peak_gap=gap | |
| if above and m60 > peak_m60: peak_m60=m60 | |
| _life_hit = bool(life) and L < life | |
| if _life_hit and not LIFELINE_KEEP_MTN: | |
| ev.append((d,'์๋ช ์ ์ดํโ์คํจ',f'์ ๊ฐ {L:,.0f} < ์๋ช ์ {life:,.0f}')) | |
| phase='WAIT'; start_ma60=HI; start_bb60=None; start_pxlow=HI; start_bbpx=None | |
| else: | |
| # โ LIFELINE_KEEP_MTN: ์์ (์ ๋๋งค๋)์ ํ๋ ๋์ฐ ๊ตฌ์กฐ๋ ํ๊ธฐํ์ง ์๊ณ ๊ณ์ ๊ด์ฐฐ. | |
| # ๋์ค์ 60์ ์ด 240 ์๋ก ๋ณต๊ทํ ๋ '๋์ง์ 60์ > ์์์ 60์ '์ด๋ฉด ๋์ฐ์ฑ๊ณต ์ธ์ . | |
| if _life_hit and not life_broken: | |
| # โ hold ์ฒดํฌ ํ์: ์ฌ๋ฒ ์ด์ค ์งํ ๋ฑ ๋ณด์ ๋ฌผ๋์ด ์์ผ๋ฉด '์ ๋๋งค๋'๊ฐ ์๋๋ผ ๋จ์ ์ดํ ๊ธฐ๋ก. | |
| # (์ ๊ทธ๋ฌ๋ฉด full_debug๊ฐ ์ ๋ น ์์ ์ P&L์ ๋ฃ์) | |
| _t = '์๋ช ์ ์ดํโ์ ๋๋งค๋(๋์ฐ์ ์ง)' if hold else '์๋ช ์ ์ดํ(๋ฏธ๋ณด์ ยท๋์ฐ์ ์ง)' | |
| if hold: held=False # ์ค์ ์ ๋๋งค๋ โ ์ค๋ณด์ ์ฒญ์ฐ | |
| life_broken=True; hold=False | |
| ev.append((d,_t, | |
| f'์ ๊ฐ {L:,.0f} < ์๋ช ์ {life:,.0f} โ ' | |
| + ('์์ ํ๋' if _t.startswith('์๋ช ์ ์ดํโ์ ๋') else '๋ณด์ ๋ฌผ๋ ์์(๋งค๋ ์๋).') | |
| + ' ๋์ฐ ํ๊ธฐ ์ ํจ(๋์ง์ 60์ ์ด ์์์ ๋ณด๋ค ๋์ผ๋ฉด ๋์ฐ์ฑ๊ณต ์ธ์ )')) | |
| if not above: # ๋์ง์ dip | |
| if gap < endlow_gap: endlow_gap=gap | |
| if m60 < endlow_ma60: endlow_ma60=m60; endlow_bb60=bl; endlow_dt=d | |
| if L < endlow_px: endlow_px=L; endlow_bbpx=bl | |
| elif above and endlow_ma60 < HI: # 60์ 240 ์๋ก ๋ณต๊ท โ ์ฑํจ ํ์ | |
| big_peak = peak_gap >= pmin | |
| deep_end = endlow_gap <= -emin | |
| higher = endlow_ma60 > mtn_s60 # 60์ ๋ ๋์ ์ ์ | |
| if big_peak and deep_end and higher: | |
| # โ ์๋ช ์ ์ ๊นผ์์ด๋(life_broken) ๋์ง์ 60์ ์ด ์์์ ๋ณด๋ค ๋์ผ๋ฉด = ๊ตฌ์กฐ ์ ์ง โ ๋์ฐ์ฑ๊ณต. | |
| # ๋จ ์๋ช ์ ์ ์ด๋ฒ ์ ์ (๋์ง์ BBํ๋จ)์ผ๋ก ์ฌ์ค์ (์ ์๋ช ์ ์ ์ด๋ฏธ ๊นจ์ก์ผ๋ฏ๋ก ๋ฌด์๋ฏธ). | |
| _revived = life_broken | |
| if life_broken: | |
| life = min(endlow_bb60, endlow_bbpx)*(1-LIFELINE_MARGIN) | |
| life_broken=False | |
| mtn_count=1 # โ ์ ์ํ์ค์ ์ฒซ ๋์ฐ โ ์์๋ ๋งค์ ์์ | |
| vlife=None; vbreak_dt=None; vseg_i=len(buys) # โ ์ ๋์ฐ์ฑ๊ณต = ์ ์ํ์ค โ ์ ๊ฐ์์๋ช ์ ํ๊ธฐ | |
| wave_fresh=True # ๋์ฐ ์์ฑ ์งํ ๋ณต๊ท โ ๋ชจ์ ์ฒดํฌ ๋์ | |
| one_wave=False # ์ ๋์ฐ โ 1ยท2๋ฒ์งธ ๋ค ๋งค์ | |
| wave_confirmed=False; conf_logged=False; touch_logged=False; cross_up_dt=None; wave_hat=0.0 # ๋ณต๊ทํ์ธ ๋ฆฌ์ | |
| phase='WAVE'; wave=0; armed=False; b240={}; flag240up=False; hold=False; lwl=None; lwl_c=None; daily_open=False; pend=[]; up_peak=0.0; up_days=0; pend=[] | |
| mts.append({'start':d,'s60':mtn_s60,'e60':endlow_ma60,'life':life,'endpx':endlow_px,'sdt':mtn_sdt,'edt':endlow_dt}) | |
| prev_end60=endlow_ma60 # โ ๋์ง์ (1) 60์ โ ๋๋ธ๋์ฐ ๋์ง์ ๋น๊ต ๊ธฐ์ค | |
| prev_mtn_peak=peak_m60; wave_peak_ma60=0.0 # โ ์ด ๋์ฐ์ ํผํฌ(60์ ์ต๊ณ ) = ๋ค์ ํ์ ์ ๋น๊ต ๊ธฐ์ค | |
| ev.append((d,'๋์ฐ์ฑ๊ณตโ์์๋์์', | |
| f'์์์ {str(mtn_sdt)[:10]}({mtn_s60:,.0f}) โ ๋์ง์ {str(endlow_dt)[:10]}({endlow_ma60:,.0f}) โ | ๋ด์ฐ๋ฆฌ+{peak_gap*100:.1f}% [1๋ฒ์งธ ๋์ฐ]' | |
| + (f' โ ์๋ช ์ ๊นผ์์ง๋ง ๋์ง์ 60์ ์ด ์์์ ๋ณด๋ค ๋์ ๊ตฌ์กฐ์ ์ง โ ๋์ฐ์ฑ๊ณต ์ธ์ / ์๋ช ์ ์ฌ์ค์ {life:,.0f}' if _revived else ''))) | |
| elif not higher: | |
| # ๋์ง์ ์ด ์์์ ๋ณด๋ค ๋ฎ์ = ๋ ๋ฎ์ ์ ์ (๊ตฌ์กฐ ๋ถ๊ดด) โ ๊ทธ ์ ์ ์ผ๋ก ์ฌ๋ฒ ์ด์ค | |
| _os60=mtn_s60; _osdt=mtn_sdt # ์คํจํ ์ฐ์ ์์์ (๋น๊ต์ฉ, ๋ฎ์ด์ฐ๊ธฐ ์ ์ ์ฅ) | |
| _drop=(endlow_ma60/_os60-1)*100 if _os60 else 0 | |
| mtn_s60 = endlow_ma60; mtn_sdt = endlow_dt | |
| life = min(endlow_bb60, endlow_bbpx)*(1-LIFELINE_MARGIN) | |
| mtn_count=0 # ๊ตฌ์กฐ ๋ถ๊ดด โ ์ํ์ค ๋ฆฌ์ | |
| life_broken=False | |
| ev.append((d,'๋์ฐ์คํจโ์ฌ๋ฒ ์ด์ค(๋๋ฎ์์ ์ )', | |
| f'์ด ์ฐ ์คํจ โ ์์ ๋ฐ๋ฅ {str(_osdt)[:10]}(60์ {_os60:,.0f})๋ณด๋ค ๋ ๋ฐ๋ฅ {str(endlow_dt)[:10]}(60์ {endlow_ma60:,.0f})์ด {_drop:+.1f}% ๋ ๋ฎ์(=๋ ๋ฎ์ ์ ์ , ๊ตฌ์กฐ ๋ถ๊ดด). ์ด ๋ฎ์ ๋ ๋ฐ๋ฅ์ ์ ์์์ ์ผ๋ก ์ฐ ๋ค์ ๊ทธ๋ฆผ / ์ ์๋ช ์ {life:,.0f}')) | |
| peak_gap=gap; peak_m60=m60; endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI # ์ ์ฐ๋ด์ฐ๋ฆฌ๋ก | |
| else: | |
| # ๋ด์ฐ๋ฆฌ/๋์ง์ ์ด ์์(๋ ธ์ด์ฆ) โ ๋ฌด์ํ๊ณ ๊ฐ์ PEAK ์ ์ง | |
| why=[] | |
| if not big_peak: why.append(f'๋ด์ฐ๋ฆฌ+{peak_gap*100:.2f}%<{pmin*100:.1f}%') | |
| if not deep_end: why.append(f'๋์ง์ {endlow_gap*100:.2f}%>-{emin*100:.1f}%') | |
| ev.append((d,'๋์ฐ์๋(๋ ธ์ด์ฆ/์์)โ๋ฌด์',' / '.join(why))) | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI | |
| elif phase=='WAVE': | |
| if life and L < life: | |
| tag='์๋ช ์ ์ดํโ์ ๋๋งค๋' if hold else '์๋ช ์ ์ดํโ๋งค๋' | |
| ev.append((d,tag,f'์ ๊ฐ {L:,.0f} < ์๋ช ์ {life:,.0f}')) | |
| if hold: held=False # ์ค์ ์ ๋๋งค๋ โ ์ค๋ณด์ ์ฒญ์ฐ | |
| phase='WAIT'; hold=False; start_ma60=HI; start_bb60=None; start_pxlow=HI; start_bbpx=None | |
| elif m60 < m240*(1-CYCLE_END): | |
| ev.append((d,'์ฌ์ดํด์ข ๋ฃ',f'60MA {m60:,.0f} < 240 -{CYCLE_END*100:.0f}%')) | |
| phase='WAIT'; hold=False; start_ma60=HI; start_bb60=None; start_pxlow=HI; start_bbpx=None | |
| elif (daily or MINBAR_STATE) and m60 < m240*(1-DOUBLE_MTN_PCT): | |
| # โ ๋๋ธ๋์ฐ ํ๋ณด: ์์๋ ์ค 60์ ์ด 240์ -2% ์๋๋ก โ ๋์ง์ (2) ์ถ์ ์์ (์ผ๋ด+๋ถ๋ด) | |
| # ๋์ค์ 60์ ์ด 240 ์๋ก ๋ณต๊ทํ ๋ ํ์ : | |
| # ๋์ง์ (2) < ๋์ง์ (1) โ ๋๋ธ๋์ฐ ํ์ (๋งค์ ์ค๋จ, ์์๋ ๋ฆฌ์ ) | |
| # ๋์ง์ (2) โฅ ๋์ง์ (1) โ ๊ทธ๋ฅ ๋๋ฆผ (์์๋ ๊ณ์) | |
| phase='DIP' | |
| dip_ma60=m60; dip_bb60=bl; dip_px=L; dip_bbpx=bl; dip_dt=d; dip_gap=gap | |
| dip_start_dt=d; dip_start_i=i # โ DIP ์์์ผ/๋ด (์ฌ๋ฒ ์ด์ค ๊ธฐ๊ฐํ์ : ์ผ๋ด=์ผ์/๋ถ๋ด=๋ด์) | |
| dip_bbmin=bl # โ DIP ๊ตฌ๊ฐ ์ต์ BBํ๋จ ์ถ์ ์์ | |
| dip_floor_logged=False | |
| dip_peak=wave_peak_ma60 # DIP ์ง์ ๊น์ง = ์ด๋ฒ ์ฐ์ ํผํฌ ํ์ | |
| ev.append((d,'๋์ง์ (2) ์ถ์ ์คโ๋งค์์ค๋จ', | |
| f'๋งค์ ์ ์ ๋ฉ์ถค โ 60์ {m60:,.0f}์ด 240์ ๋ฐ์ผ๋ก -{DOUBLE_MTN_PCT*100:.0f}% ๋๊ฒ ๊บพ์. ์ฌ๊ธฐ๊ฐ ์ด๋ฒ ๋๋ฆผ ์์({str(d)[:10]}). ๋๋ธ๋์ฐ์ธ์ง ๋ฌด๋์ง ๊ฑด์ง ์ง์ผ๋ณด๋ ์ค')) | |
| elif above or (daily and WAVE_TOUCH_BUY and gap <= 0): | |
| # above=60>240 (์ ์ ์์๋) / gap<=0 = ์ค์นจ๊ตฌ๊ฐ(240์ ์๋ก ๋ชป ์ฌ๋ผ๊ฐ, DIP ์ ) โ ๋ฐ๋ฅ ๋ค์ง๊ธฐ๋ก ๋ณด๊ณ ๋งค์ ํ์ฉ | |
| if m60 > wave_peak_ma60: wave_peak_ma60 = m60 # ํ์ฌ ์์๋ ๊ตฌ๊ฐ์ 60์ ์ต๊ณ (ํผํฌ)๋ง ์ถ์ | |
| if gap > wave_hat: wave_hat = gap # โ ์ด ์์๋์์ ๋ง๋ ์ต๋ gap (๋ชจ์ ๋ด์ฐ๋ฆฌ ๊ฐ์ง) | |
| if daily: | |
| # ===== ์ผ๋ด: '์์๋' = 60์ ์๋๋ก ๋ด๋ ค๊ฐ ๊ตฌ๊ฐ(์ํผ์๋) ===== | |
| bnc = (i>0 and C > float(c.iloc[i-1])) # ๋ฐ๋ฑ์ผ(์ข ๊ฐ>์ ์ผ์ข ๊ฐ) โ ๊น์ ๊ตฌ๊ฐ ๋งค์ ๊ฒ์ดํธ | |
| # โ ๋ณต๊ท ํ์ธ: 60์ ์ด 240์ ์๋ก WAVE_CONFIRM_GAP ๋๋ฌํด์ผ ๋งค์ ์์ (๊ฐ์ง ๋ณต๊ท=๋ชจ์ํํ ์ฐจ๋จ) | |
| if WAVE_CONFIRM_GAP and not wave_confirmed: | |
| if gap >= WAVE_CONFIRM_GAP: | |
| wave_confirmed = True | |
| ev.append((d,'์์๋ ํ์ธ(์ง์ง ๋ณต๊ท)', | |
| f'60์ ์ด 240์ +{gap*100:.1f}% โฅ +{WAVE_CONFIRM_GAP*100:.0f}% ๋๋ฌ โ ์์๋ ๋งค์ ์์')) | |
| elif gap > 0: | |
| # gap์ด +7% ๋ฏธ๋ฌ์ด๋ผ๋ 60์ ์ด 240์ ์์์ '์ฐ์ ์์กด'ํ ์ผ์๋ก ํ์ธ (์๊ฐ ๊ธฐ๋ฐ, OR) | |
| if cross_up_dt is None: cross_up_dt = d # 240 ์๋ก ์ฌ๋ผ์จ ์ฒซ๋ ์ต์ปค | |
| surv = (d - cross_up_dt).days | |
| if WAVE_CONFIRM_DAYS and surv >= WAVE_CONFIRM_DAYS: | |
| wave_confirmed = True | |
| ev.append((d,'์์๋ ํ์ธ(์๊ฐ์์กด)', | |
| f'60์ ์ด 240์ ์(gap {gap*100:+.1f}%)์์ {surv}์ผ ์ฐ์ ์์กด โฅ {WAVE_CONFIRM_DAYS}์ผ โ ์ง์ง ๋ณต๊ท(๋ชจ์ ์๋) โ ์์๋ ๋งค์ ์์')) | |
| elif not conf_logged: | |
| conf_logged = True | |
| ev.append((d,'๋ณต๊ทํ์ธ ๋๊ธฐ(๋งค์๋ณด๋ฅ)', | |
| f'60์ +{gap*100:.1f}% < +{WAVE_CONFIRM_GAP*100:.0f}% โ ๊ฐ์ง ๋ณต๊ท(๋ชจ์) ๊ฐ๋ฅ์ฑ, gap๋๋ฌ or {WAVE_CONFIRM_DAYS}์ผ์์กด๊น์ง ๋งค์ ์ ํจ')) | |
| else: | |
| cross_up_dt = None # 240 ์๋๋ก(gapโค0) ๋๊บพ์ โ ์๊ฐ ์นด์ดํฐ ๋ฆฌ์ (๊ฐ์ง ๊ฐ๋ฅ์ฑ) | |
| # โ ์ค์นจ๊ตฌ๊ฐ(gap โค 0): ๋ชจ์๊ฐ '์์ง ์ ์๊ธด' ๊ฒ = ๋ฐ๋ฅ ๋ค์ง๊ธฐ โ ํ์ธ ์์ด๋ ๋งค์ ํ์ฉ. | |
| # ๋จ ์ด ์์๋์์ ์ด๋ฏธ ๋ชจ์ ๋ด์ฐ๋ฆฌ(gap โฅ WAVE_HAT_PCT)๋ฅผ ๋ง๋ ๋ค ๋ด๋ ค์จ ๊ฑฐ๋ฉด | |
| # ๊ทธ๊ฑด ๋ฐ๋ฅ์ด ์๋๋ผ ๋ชจ์ ํ๊ฐ๋ค๋ฆฌ โ ์ค์นจ ๋งค์ ๊ธ์ง(ํ์ธ ๋๊ธฐ ์ ์ง). | |
| _hat_formed = (WAVE_HAT_PCT and wave_hat >= WAVE_HAT_PCT | |
| and ((not WAVE_HAT_ONLY_FRESH) or wave_fresh)) | |
| _touch = (WAVE_TOUCH_BUY and gap <= 0 and not _hat_formed) | |
| if WAVE_HAT_PCT and gap <= 0 and _hat_formed and not conf_logged: | |
| conf_logged = True | |
| ev.append((d,'๋ณต๊ทํ์ธ ๋๊ธฐ(๋ชจ์ํ๊ฐ)', | |
| f'์ด ์์๋์์ 60์ ์ด 240์ +{wave_hat*100:.1f}%๊น์ง ์ฌ๋๋ค ๋ด๋ ค์ด(๋ชจ์ ๋ด์ฐ๋ฆฌ) โ ์ค์นจ ์๋, ๋งค์ ๋ณด๋ฅ')) | |
| _can_buy = (not WAVE_CONFIRM_GAP) or wave_confirmed or _touch | |
| if _touch and not touch_logged: | |
| touch_logged = True | |
| ev.append((d,'์ค์นจ๊ตฌ๊ฐ(๋ฐ๋ฅ) ๋งค์ํ์ฉ', | |
| f'60์ {gap*100:+.1f}% โค 0 (240์ ์ค์นจ/์๋) โ ๋ชจ์ ์๋ โ ์์๋ ๋งค์ ์งํ')) | |
| if daily_open: | |
| if C < m60: | |
| # ์๋ ๊ตฌ๊ฐ ์งํ ์ค โ ๋งค์ผ ๋งค์ (์๋ก ํ์๋ค ์๋ ๊ฐ์ ๊ตฌ๊ฐ) | |
| amt = d1 if wave==1 else d2; hold=True | |
| if WAVE_BUY_FLOOR and C < m240*(1-WAVE_BUY_FLOOR) and not bnc: | |
| # 240์ -8% ์๋ + ํ๋ฝ(๋ฐ๋ฑ ์๋) โ ๋ณด๋ฅ. ๋ฐ๋ฑํ๋ฉด ๊ทธ๋ ๋งค์ ์ฌ๊ฐ | |
| ev.append((d,' โ๋งค์๋ณด๋ฅ(240 -{:.0f}%ยท๋ฐ๋ฑ๋๊ธฐ)'.format(WAVE_BUY_FLOOR*100), | |
| f'์ข ๊ฐ {C:,.0f} < 240 -{WAVE_BUY_FLOOR*100:.0f}% ({(C/m240-1)*100:+.1f}%) ํ๋ฝ์ค โ ์์๋ ์์๋ค, ๋ฐ๋ฑ์ ์ฌ๊ฐ')) | |
| elif _buy(d,C,amt,f'{wave}๋ฒ ์ผ๋ด๋งค์', force=(daily and WAVE_OVERRIDE_COOLDOWN)): | |
| _deeptag = ' (240 -{:.0f}%โ ๋ฐ๋ฑํ์ธ)'.format(WAVE_BUY_FLOOR*100) if (WAVE_BUY_FLOOR and C < m240*(1-WAVE_BUY_FLOOR)) else '' | |
| ev.append((d,' โ๋งค์',f'{amt:,}์ (์ผ๋ด ๋ฐ๋ณต){_deeptag}')) | |
| up_peak=0.0; up_days=0 # ์๋๋ก ๋ด๋ ค์ค๋ฉด ์ ์ต๊ณ ์น/์ฐ์์ผ์ ๋ฆฌ์ | |
| else: | |
| # 60์ ์ โ ์ต๊ณ ์์น๋ฅ + ์ฐ์์ผ์ ์ถ์ (๋งค์๋ ์ผ) | |
| rise = C/m60 - 1 | |
| if rise > up_peak: up_peak = rise | |
| up_days += 1 | |
| if up_peak >= WAVE_EXIT_PCT and up_days >= WAVE_UP_DAYS: | |
| # ์ถฉ๋ถํ ๋์ด + ์ถฉ๋ถํ ์ค๋ ์์ ์์ โ ์ด ์์๋(์๋ ๊ตฌ๊ฐ) ์ข ๋ฃ | |
| daily_open=False; pend=[] | |
| ev.append((d,f'{wave}๋ฒ์งธ ์๋ ์ข ๋ฃ', | |
| f'์ ๊ตฌ๊ฐ ์ต๊ณ +{up_peak*100:.1f}% โฅ +{WAVE_EXIT_PCT*100:.0f}%, {up_days}์ผ ์ฐ์ ์ โ ์ง์ง ์ ๊ตฌ๊ฐ')) | |
| if wave>=2: | |
| flag240up=True | |
| ev.append((d,'240์ฌ(์ถ๋งค์ข ๋ฃ)','2๋ฒ์งธ ์์๋ ๋ โ ๋ค์ 240์ ์๋๋ก ๊บพ์ผ ๋๊น์ง ๋๊ธฐ')) | |
| up_peak=0.0; up_days=0 | |
| else: | |
| # ์๋ ๊ตฌ๊ฐ ๋๊ธฐ ์ค | |
| if flag240up: | |
| pass | |
| elif C < m60 and _can_buy: | |
| # 60์ ์๋๋ก ๋ด๋ ค์ด โ ํ๋ณด ๊ตฌ๊ฐ์ ์์๋ (์์ง ํ์ ์ ). bnc=๊ทธ๋ ๋ฐ๋ฑ์ฌ๋ถ | |
| pend.append((d,C,m240,bnc)) | |
| first_wave = (wave==0) | |
| deep_ok = first_wave or (C <= m60*(1-WAVE_DEEP_PCT)) | |
| if deep_ok: | |
| # ํ์ ! โ ์ ์์๋ ์์, ๋ชป ์ฐ ๋ ๋ค ์๊ธ ๋งค์ | |
| wave+=1 | |
| _cap = 1 if (DEEP_DIP_ONE_WAVE and one_wave) else 2 | |
| if wave<=_cap: | |
| daily_open=True; hold=True | |
| amt = d1 if wave==1 else d2 | |
| d0 = pend[0][0] | |
| tagdeep = '' if first_wave else f' (๊น์ด {(C/m60-1)*100:+.1f}% โค -{WAVE_DEEP_PCT*100:.0f}%)' | |
| ev.append((d,f'{wave}๋ฒ์งธ ์๋ ์์', | |
| f'์ข ๊ฐ {C:,.0f} < 60์ {m60:,.0f}{tagdeep}')) | |
| if len(pend)>1: | |
| ev.append((d,' โ์๊ธ๋งค์',f'{str(d0)[:10]}๋ถํฐ ๋ชป์ฐ {len(pend)-1}์ผ์น ํจ๊ป ๋งค์')) | |
| for pd_, pc_, pm_, pb_ in pend: | |
| if WAVE_BUY_FLOOR and pc_ < pm_*(1-WAVE_BUY_FLOOR) and not pb_: | |
| ev.append((pd_,' โ๋งค์๋ณด๋ฅ(240 -{:.0f}%ยท๋ฐ๋ฑ๋๊ธฐ)'.format(WAVE_BUY_FLOOR*100), | |
| f'์ข ๊ฐ {pc_:,.0f} < 240 -{WAVE_BUY_FLOOR*100:.0f}% ({(pc_/pm_-1)*100:+.1f}%) ํ๋ฝ์ค')) | |
| elif _buy(pd_,pc_,amt,f'{wave}๋ฒ ์ผ๋ด๋งค์', force=(daily and WAVE_OVERRIDE_COOLDOWN)): | |
| ev.append((pd_,' โ๋งค์',f'{amt:,}์ (๋งค์ผ {amt//10000}๋ง)')) | |
| pend=[] | |
| else: | |
| flag240up=True; pend=[] | |
| ev.append((d,'240์ฌ(์ถ๋งค์ข ๋ฃ)','2๋ฒ์งธ ์์๋ ํ โ ๋ค์ 240์ ์๋๋ก ๊บพ์ผ ๋๊น์ง ๋๊ธฐ')) | |
| else: | |
| # 60์ ์๋ก ๋ณต๊ท โ ํ์ ๋ชปํ ํ๋ณด ๊ตฌ๊ฐ์ ๋ฒ๋ฆผ (์ด์ง ์ค์นจ์ด์์) | |
| if pend: pend=[] | |
| else: | |
| # ===== ๋ถ๋ด/์๊ฐ๋ด: ๊ธฐ์กด ํฐ์น ๊ธฐ๋ฐ (๊ฒ์ฆ๋จ, ๋ณ๊ฒฝ ์์) ===== | |
| if armed and L <= m60: | |
| wave+=1; armed=False; lwl=L; lwl_c=C | |
| if wave <= 2: | |
| ev.append((d,f'{wave}๋ฒ์งธ ์์๋',f'์ ๊ฐ {L:,.0f} โค 60MA {m60:,.0f}')) | |
| mlt=2 if wave==1 else 1; amt=a*mlt; hold=True | |
| if _buy(d,C,amt,f'{wave}๋ฒ 60์๋ x{mlt}'): | |
| ev.append((d,' โ๋งค์',f'{amt:,}์')) | |
| elif not flag240up: | |
| flag240up=True | |
| ev.append((d,'240์ฌ(์ถ๋งค์ข ๋ฃ)','2๋ฒ์งธ ์์๋ ํ โ ๋ค์ 240์ ์๋๋ก ๊บพ์ผ ๋๊น์ง ๋๊ธฐ')) | |
| # 240๊ทผ์ฒ = ๋๊ธฐ ์ง์ ๊ฐ ์ฃผ๋ฌธ. ํ์ฌ ์์๋ ์ฌ๋กฏ๋ง ์ฒด๊ฒฐ. | |
| # ์ด์ ์์๋ ๋ฏธ์ฒด๊ฒฐ๋ถ์ ์๋ฉธ(ํ๋ฝ ๋ฌผํ๊ธฐ ๋ฐฉ์ง). | |
| # โ 240์ฌ(3๋ฒ์งธ ์) ๋๋ฉด ๋๊ธฐ์ฃผ๋ฌธ๋ ์ทจ์ โ ์ฌ์ดํด ๊บพ์ด๋ ์ค์ ์ถ๋งค ๊ธ์ง | |
| if (not flag240up) and wave in (1,2) and not b240.get(wave): | |
| if L <= m240*(1+NEAR_240): | |
| mlt=2 if wave==1 else 1; amt=b*mlt; hold=True; b240[wave]=True | |
| if _buy(d,C,amt,f'{wave}๋ฒ 240๊ทผ์ฒ x{mlt}'): | |
| ev.append((d,' โ๋งค์',f'{amt:,}์ ({wave}๋ฒ 240๊ทผ์ฒ)')) | |
| if (not daily) and (not armed) and lwl_c and C > lwl_c*(1+recov): | |
| armed=True | |
| # 1๋ฒ์งธ ์์๋ ๋ฌด์ฅ: ๊ฐ๊ฒฉ ์ข ๊ฐ๊ฐ 60์ ์๋ก +WAVE_RISE_PCT ์ฌ๋ผ์์ผ ('์' ํ์ธ) | |
| if (not armed) and wave==0 and C > m60*(1+wrise): | |
| armed=True | |
| elif phase=='DIP': | |
| # ๋์ง์ (2) ์ถ์ ์ค (๋งค์ ์ค๋จ ์ํ) | |
| if life and L < life: | |
| tag='์๋ช ์ ์ดํโ์ ๋๋งค๋' if hold else '์๋ช ์ ์ดํโ๋งค๋' | |
| ev.append((d,tag,f'์ ๊ฐ {L:,.0f} < ์๋ช ์ {life:,.0f}')) | |
| if hold: held=False # ์ค์ ์ ๋๋งค๋ โ ์ค๋ณด์ ์ฒญ์ฐ | |
| phase='WAIT'; hold=False; start_ma60=HI; start_bb60=None; start_pxlow=HI; start_bbpx=None | |
| elif not above: | |
| # ๋์ง์ (2) ์ต์ ๊ฐฑ์ (โ ์ฌ์ดํด์ข ๋ฃ ์์ โ ๋์ง์ (1)๊ณผ ๋์ผํ๊ฒ ์๋ช ์ ๋ง์ด ์ฌ๋ง์กฐ๊ฑด) | |
| if gap < dip_gap: dip_gap=gap | |
| if bl < dip_bbmin: dip_bbmin=bl # โ DIP ๊ตฌ๊ฐ ์ต์ BBํ๋จ | |
| if m60 < dip_ma60: dip_ma60=m60; dip_bb60=bl; dip_dt=d | |
| if L < dip_px: dip_px=L; dip_bbpx=bl | |
| # โ ์กฐ๊ธฐ '์์' ํ์ : 240 ๋ณต๊ท๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ , ๊น์ด๊ฐ ์๊ณ๋ฅผ ์ฐ๋ ์๊ฐ ์ฌ๋ฒ ์ด์ค. | |
| # ๋จ ๋๋ธ๋์ฐ ๊ฐ๋ฅ์ฑ์ด ์ด์์์ผ๋ฉด(ํผํฌ๊ฐญ ๋ฐด๋ ์ + ๋์ง์ ์ ์ง) ๋ฐ๋ ์ ํจ โ ๋๋ธ๋์ฐ ๋ณด์กด. | |
| # โ ๊ธฐ๊ฐ ๊ธฐ๋ฐ(REBASE_DIP_DAYS)๋ ๊ฐ์ ์๋ฆฌ์์ OR๋ก ํ์ : | |
| # ๊น์ด๊ฐ ์์๋ DIP๊ฐ ๋๋ฌด ์ค๋ ์ง์๋๋ฉด(2๋ ์ง๋ฆฌ ๋๋ฆผ=๋๋ฆผ ์๋) ์ฌ๋ฒ ์ด์ค. | |
| _deep_hit = bool(REBASE_EARLY and REBASE_DEEP_PCT and dip_gap <= -REBASE_DEEP_PCT) | |
| _elapsed = ((d - dip_start_dt).days if daily else (i - dip_start_i)) if dip_start_dt is not None else 0 | |
| _long_hit = bool(REBASE_DIP_DAYS and dip_start_dt is not None and _elapsed >= REBASE_DIP_DAYS) | |
| if _deep_hit or _long_hit: | |
| _p0 = dip_peak if dip_peak > 0 else None | |
| _pg0 = (_p0/prev_mtn_peak - 1) if (prev_mtn_peak and _p0) else None | |
| _eg0 = (dip_ma60/prev_end60 - 1) if prev_end60 else None | |
| _endok0 = (_eg0 is None) or (_eg0 >= -DOUBLE_MTN_END_TOL) | |
| _floor0 = bool(DOUBLE_MTN_LIFELINE_FLOOR and life and dip_px > life) # ์ ๊ฐ๊ฐ ์ฒซ ์๋ช ์ ์ | |
| _band0 = (DOUBLE_MTN_GAP > 0 and _pg0 is not None | |
| and DOUBLE_MTN_GAP_MIN <= _pg0 < DOUBLE_MTN_GAP) | |
| _dbl_strict = _band0 and _endok0 # ์๋ ๊ธฐ์ค(๋์ง์ ์ ์ง) | |
| _dbl_floor = _band0 and (_endok0 or _floor0) # +์๋ช ์ ๋ฐ๋ฅ ์ธ์ | |
| # โ ๊น์ด(-25%) ํธ๋ฆฌ๊ฑฐ๋ ์๋ช ์ ๋ฐ๋ฅ๊น์ง ์ธ์ ํด์ ๋ง๊ณ (030530 ๋ณดํธ: ์ ๊ฐ>์ฒซ์๋ช ์ ์ด๋ฉด ์ํ์ค ์์กด), | |
| # ์ฅ๊ธฐ(600์ผ) ํธ๋ฆฌ๊ฑฐ๋ ์๋ ๊ธฐ์ค๋ง์ผ๋ก ๋ง์(236200 ์ ์ง: ์๊ฐ์ด ์๋ช ์ ๋ฐ๋ฅ๋ณด๋ค ์ฐ์ ). | |
| _blk = _dbl_floor if (_deep_hit and not _long_hit) else _dbl_strict | |
| if _deep_hit and (not _long_hit) and _floor0 and (not _endok0) and _blk and (not dip_floor_logged): | |
| dip_floor_logged=True | |
| ev.append((d,'์กฐ๊ธฐ์ฌ๋ฒ ์ด์ค ๋ณด๋ฅ(์๋ช ์ ๋ฐ๋ฅ)', | |
| f'๋ค์ ์ ๊ทธ๋ฆผ โ ๋ง์ด({dip_gap*100:.1f}%) ๋น ์ก์ง๋ง ์ต์ ๊ฐ {dip_px:,.0f}๊ฐ ์ฒซ ์๋ช ์ {life:,.0f}๋ณด๋ค ์. ์์ง ๊ตฌ์กฐ ์ ๋ฌด๋์ง โ ๋๋ธ๋์ฐ ๊ฐ๋ฅ์ฑ ์ด๋ ค๋ ')) | |
| if not _blk: | |
| _why0 = ('๊ณ๋จ์ํ๋ฝ' if (_pg0 is not None and _pg0 < DOUBLE_MTN_GAP_MIN) | |
| else ('๋์ง์ ๋ฎ์์ง' if not _endok0 | |
| else ('ํผํฌ์ ์ง' if _pg0 is not None else 'ํผํฌ๋ฐ์ดํฐ ์์'))) | |
| mtn_s60 = dip_ma60; mtn_sdt = dip_dt | |
| life = min(dip_bb60, dip_bbpx)*(1-LIFELINE_MARGIN) | |
| _dur0 = _elapsed # ์ผ๋ด=์ผ์ / ๋ถ๋ด=๋ด์ | |
| if _deep_hit: | |
| _tag0 = '์์โ์ฌ๋ฒ ์ด์ค(์กฐ๊ธฐํ์ )' | |
| _rsn0 = f'60์ ์ด 240์ ๋ฐ์ผ๋ก {dip_gap*100:.1f}% ๊น์ด ๋น ์ง(๊ธฐ์ค -{REBASE_DEEP_PCT*100:.0f}% ์ดํ). ๋๋ฆผ ์์ {str(dip_start_dt)[:10]}๋ถํฐ {_dur0}์ผ์งธ' | |
| else: | |
| _tag0 = '์์โ์ฌ๋ฒ ์ด์ค(์ฅ๊ธฐDIP)' | |
| _rsn0 = (f'๋๋ฆผ์ด {_dur0}์ผ(์ฝ {_dur0//365}๋ )์ด๋ ์ง์({str(dip_start_dt)[:10]}~{str(d)[:10]}, ๊ธฐ์ค {REBASE_DIP_DAYS}์ผ). ' | |
| f'๊น์ด๋ {dip_gap*100:.1f}%๋ก ์์๋ ์ด๋ ๊ฒ ์ค๋ ๋๋ฉด ๋๋ฆผ ์๋๋ผ ๋ฌด๋์ง') | |
| ev.append((d,_tag0, | |
| f'์์์ โ ์ฒ์๋ถํฐ ๋ค์ โ {_rsn0} / ์ด๋ฒ ๋ด์ฐ๋ฆฌ๋ ์ง์ ์ฐ๋ณด๋ค {(_pg0*100 if _pg0 is not None else 0):+.1f}%({_why0}) / 240์ ๋ณต๊ท ์ ๊ธฐ๋ค๋ฆฌ๊ณ ์์๋ ์ ์ / ์ ์์์ {str(dip_dt)[:10]}(60์ {dip_ma60:,.0f}) / ์ ์๋ช ์ {life:,.0f}')) | |
| phase='PEAK'; peak_gap=gap; peak_m60=m60; vlife=None; vbreak_dt=None; vseg_i=len(buys) # โ ์ ์ฐ ๊ทธ๋ฆฌ๊ธฐ ์์ โ ์ ๊ฐ์์๋ช ์ ํ๊ธฐ | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI | |
| hold=False; wave=0; armed=False; b240={}; flag240up=False | |
| daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| mtn_count=0; life_broken=False | |
| wave_confirmed=False; conf_logged=False; touch_logged=False; cross_up_dt=None; wave_hat=0.0 | |
| prev_mtn_peak=_p0; wave_peak_ma60=0.0 | |
| else: | |
| # 60์ ์ด 240 ์๋ก ๋ณต๊ท โ ๋๋ธ๋์ฐ์ธ์ง ํ์ (ํผํฌ ๊ฐญ ๊ธฐ์ค) | |
| # ์ง์ ์ฐ ํผํฌ(prev_mtn_peak) vs ์ด๋ฒ ์ฐ ํผํฌ(dip_peak, DIP ์ง์ ๊น์ง์ ์ต๊ณ ) | |
| # dip_dt(๋์ง์ 2) ์ด์ ์ ๋ง์ง๋ง ํผํฌ = ์ด๋ฒ ์ฐ ํผํฌ | |
| # ๊ทธ ํผํฌ ์ด์ ์ ํผํฌ = ์ง์ ์ฐ ํผํฌ (์ฌ์ ๊ณ์ฐ ๋ฆฌ์คํธ์์ ์กฐํ โ ์ํ์ถ์ ์ ํจ) | |
| # โ ๋์ฐ ๋จ์ ํผํฌ ๋น๊ต (์ธ๊ทธ๋จผํธ ์กฐ๊ฐ ์๋): | |
| # ์ด๋ฒ ์ฐ ํผํฌ = dip_peak (DIP ์ง์ ์ง์ ๊น์ง WAVE์์ ์ถ์ ํ 60์ ์ต๊ณ ) | |
| # ์ง์ ์ฐ ํผํฌ = prev_mtn_peak (์ง์ ๋์ฐ์ฑ๊ณต/๋๋ธ๋์ฐ์ ํผํฌ) | |
| p_now = dip_peak if dip_peak > 0 else None | |
| p_prev = prev_mtn_peak | |
| _pg = (p_now/p_prev - 1) if (p_prev and p_now) else None | |
| _deep = dip_gap <= -emin # ๋์ง์ (2)์ด ์ง์ง ๋์ง์ ๊ธ ๊น์ด(-7%)์ธ๊ฐ | |
| _eg = (dip_ma60/prev_end60 - 1) if prev_end60 else None # ๋์ง์ (2) vs ๋์ง์ (1) | |
| _end_ok = (_eg is None) or (_eg >= -DOUBLE_MTN_END_TOL) # ๋์ง์ ์ ์ง/์์น? | |
| _floor_ok = bool(DOUBLE_MTN_LIFELINE_FLOOR and life and dip_px > life) # โ ์ ๊ฐ๊ฐ ์ฒซ ์๋ช ์ ์ = ์ํ์ค ์์กด | |
| ev.append((d,' โDIPํ์ ๋ฐ์ดํฐ', | |
| f'ํผํฌ๊ฐญ {(_pg*100 if _pg is not None else 0):+.1f}% / ๋๊น์ด {dip_gap*100:.2f}% / ๋์ง์ ๊ฐญ {(_eg*100 if _eg is not None else 0):+.1f}%')) | |
| # โป _floor_ok(์ ๊ฐ>์ฒซ์๋ช ์ )๋ '๋๋ธ๋์ฐ ํ์ '์๋ ์ ์ โ ๋๋ธ๋ก ์น๋ฉด ์นด์ดํธ๊ฐ ์ฌ๋ผ | |
| # ํธ๋ฆฌํ์บก์ ๊ฑธ๋ ค ์ดํ ๋งค์๊ฐ ์ฃฝ๋ ๋ถ์์ฉ(ํ์ด๋์ค2023 ์ฌ๊ฐ ์ผ์ด์ค ๋ค์งํ, ์ ์ -129M ํ์ธ). | |
| # ๋์ ์กฐ๊ธฐ์ฌ๋ฒ ์ด์ค๋ฅผ ๋ง๋ ๋ฐ๋ง ์จ์, ๋์ง์ ๋ฎ์์ง+์๋ช ์ ์ ์ผ์ด์ค๋ '์์๋ ์ฌ๊ฐ'(์นด์ดํธ ์ ์ง)๋ก ํ๋ฆผ. | |
| if DOUBLE_MTN_GAP > 0 and _pg is not None and DOUBLE_MTN_GAP_MIN <= _pg < DOUBLE_MTN_GAP and _deep and _end_ok: | |
| # โ ๋๋ธ๋์ฐ ํ์ : ํผํฌ๊ฐ ๋ณ๋ก ์ ์ฌ๋์(์ ์๋ฆฌ) โ ์์๋ ๋ฆฌ์ , ์ ๋์ฐ | |
| # โป ์๋ช ์ (life)์ ๋ฆฌ์ ํ์ง ์์ โ ์ฒซ ๋์ฐ ์์์ ์๋ช ์ ์ ์ํ์ค ๋ด๋ด ๊ทธ๋๋ก ์ ์ง. | |
| # (๋๋ธ/ํธ๋ฆฌํ ๋๋ฆผ์์ ์๊ฒ ํ๋ ค๋๊ฐ์ง ์๋๋ก. ์ฌ๋ฒ ์ด์ค๋ ์ ์ํ์ค๋ผ ๋ณ๋) | |
| gap_pct = (p_now/p_prev - 1)*100 | |
| # โ ์๋ช ์ ์ฌ๋ ค์ก๊ธฐ: ๋ฐฉ๊ธ ์ง๋์จ DIP ๊ตฌ๊ฐ ์ต์ BBํ๋จ ๊ธฐ์ค (๋ด๋ฆฌ์ง๋ ์์) | |
| _oldlife = life | |
| if daily and VIRTUAL_LIFELINE and dip_bbmin < HI: | |
| _v = dip_bbmin*(1-LIFELINE_MARGIN) | |
| vlife = _v if (life is None or _v > life) else None | |
| vseg_i = len(buys); vbreak_dt=None | |
| one_wave = bool(DEEP_DIP_ONE_WAVE and dip_gap <= -DEEP_DIP_ONE_WAVE_PCT) | |
| if DOUBLE_MTN_RELIFE and dip_bbmin < HI: | |
| _cand = dip_bbmin*(1-LIFELINE_MARGIN) | |
| if life is None or _cand > life: life = _cand | |
| mtn_s60 = dip_ma60; mtn_sdt = dip_dt | |
| mts.append({'start':d,'s60':mtn_s60,'e60':dip_ma60,'life':life, | |
| 'endpx':dip_px,'sdt':mtn_sdt,'edt':dip_dt}) | |
| mtn_count += 1 # โ ๋์ฐ ์๋ฒ ์ฆ๊ฐ (2=๋๋ธ, 3=ํธ๋ฆฌํโฆ) | |
| wave_confirmed=False; conf_logged=False; touch_logged=False; cross_up_dt=None; wave_hat=0.0 # ๋ณต๊ทํ์ธ ๋ฆฌ์ | |
| _over = mtn_count > MAX_MTN_WAVE # ํธ๋ฆฌํ(3)+ ์ด๋ฉด ์์๋ ๋งค์ ์ ํจ | |
| _nm = {2:'๋๋ธ',3:'ํธ๋ฆฌํ',4:'์ฟผ๋๋ฌํ'}.get(mtn_count, f'{mtn_count}์ค') | |
| ev.append((d,f'{_nm}๋์ฐ ํ์ โ์์๋ ๋ฆฌ์ ', | |
| f'ํผํฌ๊ฐญ {gap_pct:+.1f}% < {DOUBLE_MTN_GAP*100:.0f}% (์ ์๋ฆฌ) โ {mtn_count}๋ฒ์งธ ๋์ฐ / ์๋ช ์ {life:,.0f}' | |
| + (f'(์ง์ ๋๋ฆผ ์ต์ BB๋ก ์ฌ๋ฆผ, ๊ธฐ์กด {_oldlife:,.0f})' if (_oldlife and life>_oldlife) else '(์ฒซ๋์ฐ ์ ์ง)') | |
| + (' | โ ๋์ง์ ๋ฎ์์ก์ง๋ง ์ ๊ฐ>์ฒซ์๋ช ์ โ ๋๋ธ ์ธ์ (์๋ช ์ ๋ฐ๋ฅ)' if (_floor_ok and not _end_ok) else '') | |
| + (f' | โ {MAX_MTN_WAVE}๊ฐ ์ด๊ณผ โ ์์๋ ๋งค์ ์ ํจ(๊ด์ฐฐ๋ง)' if _over else ''))) | |
| wave_fresh=False # ์ํ์ค ์งํ ์ค(๋๋ธ๋์ฐ) โ ๋ชจ์ ๋ฌด์ | |
| phase='WAVE'; wave=0; armed=False; b240={}; flag240up=_over # โ ํธ๋ฆฌํ+ = ๋งค์ ์ต์ | |
| lwl=None; lwl_c=None; daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| prev_end60=dip_ma60 # โ ๋์ง์ (2)๊ฐ ์ ๋น๊ต ๊ธฐ์ค | |
| prev_mtn_peak=p_now; wave_peak_ma60=0.0 | |
| elif _deep and _pg is not None and _pg >= DOUBLE_MTN_GAP: | |
| # โ ํผํฌ๊ฐ ํฌ๊ฒ ์ ์ง(+42%โ)ํ ๋ค์ ๊น์ ๋๋ฆผ = ํ ์ฌ์ดํด ์์ฃผ ํ ๋ถ๊ดด โ ์ฌ๋ฒ ์ด์ค (์ ์ํ์ค) | |
| # dip ์ต์ ์ ์ ์ ๋์ฐ ์์์ ์ผ๋ก ์ผ๊ณ PEAK๋ถํฐ ๋ค์ (์ผ์ฑ 2013, ํ์ด๋์ค 2016 ์ ํ) | |
| mtn_s60 = dip_ma60; mtn_sdt = dip_dt | |
| life = min(dip_bb60, dip_bbpx)*(1-LIFELINE_MARGIN) | |
| ev.append((d,'๊น์๋๋ฆผโ์๋์ฐ(์ฌ๋ฒ ์ด์ค)', | |
| f'ํ ๋ฐํด ๋ค ๋๊ณ ๋ฌด๋์ง โ ์ฒ์๋ถํฐ ๋ค์ โ ๋ด์ฐ๋ฆฌ๊ฐ ์ง์ ์ฐ๋ณด๋ค +{_pg*100:.1f}% ํฌ๊ฒ ์ฌ๋๋ค๊ฐ(๊ธฐ์ค +{DOUBLE_MTN_GAP*100:.0f}% ๋์) 60์ ์ด 240์ ๋ฐ์ผ๋ก {dip_gap*100:.1f}% ๊น์ด ๋น ์ง(๊ธฐ์ค -{emin*100:.0f}% ์ดํ) / ์ ์์์ {str(dip_dt)[:10]}(60์ {dip_ma60:,.0f}) / ์ ์๋ช ์ {life:,.0f}')) | |
| phase='PEAK'; peak_gap=gap; peak_m60=m60; vlife=None; vbreak_dt=None; vseg_i=len(buys) # โ ์ ์ฐ ๊ทธ๋ฆฌ๊ธฐ ์์ โ ์ ๊ฐ์์๋ช ์ ํ๊ธฐ | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI | |
| hold=False; wave=0; armed=False; b240={}; flag240up=False | |
| daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| mtn_count=0; life_broken=False # ์ฌ์ดํด ์์ฃผ ํ ์ ์ํ์ค (๋ค์ ๋์ฐ์ฑ๊ณต์ด 1๋ฒ์งธ) | |
| prev_mtn_peak=p_now; wave_peak_ma60=0.0 | |
| elif _deep and REBASE_DEEP_PCT and dip_gap <= -REBASE_DEEP_PCT \ | |
| and not (_floor_ok and _pg is not None and DOUBLE_MTN_GAP_MIN <= _pg < DOUBLE_MTN_GAP): | |
| # โ ์์โ์ฌ๋ฒ ์ด์ค: ์์๋์ธ ์ค ์๊ณ ์๋๋ฐ 240์ฌ ๋ชป ๊ฐ๊ณ ๊น์ด(-REBASE_DEEP_PCTโ) ๋ถ๊ดด. | |
| # ํผํฌ๊ฐ ๊ณ๋จ์ํ๋ฝ(์ ์๋ฆฌ/ํ๋ฝ)์ด๋ผ ๋๋ธ๋์ฐ๋ ์๋ โ '์์๋ ์ฌ๊ฐ' ๋์ ๋์ฐ๋ถํฐ ๋ค์. | |
| # ๋จ(์๋ช ์ ๋ฐ๋ฅ, 030530): ์ ๊ฐ>์ฒซ์๋ช ์ + ํผํฌ๊ฐญ ๋ฐด๋ ์์ด๋ฉด ์ํ์ค ์์กด โ ์ฌ๋ฒ ์ด์ค ๋์ ์๋ '์ฌ๊ฐ'๋ก. | |
| # dip ์ต์ ์ ์ ์ ๋์ฐ ์์์ ์ผ๋ก, ์๋ช ์ ๋ ์๋ก. (John: "์์๋ ์๋์์ผ๋ ๋ค์ ๋์ฐ ๋ณด์") | |
| mtn_s60 = dip_ma60; mtn_sdt = dip_dt | |
| life = min(dip_bb60, dip_bbpx)*(1-LIFELINE_MARGIN) | |
| _whyR = ('๊ณ๋จ์ํ๋ฝ' if (_pg is not None and _pg < DOUBLE_MTN_GAP_MIN) | |
| else ('๋์ง์ ๋ฎ์์ง' if not _end_ok else 'ํผํฌ๋ฐ์ดํฐ ์์')) | |
| ev.append((d,'์์โ์ฌ๋ฒ ์ด์ค(๊น์๋ถ๊ดด)', | |
| f'์์์ โ ์ฒ์๋ถํฐ ๋ค์ โ 60์ ์ด 240์ ๋ฐ์ผ๋ก {dip_gap*100:.1f}% ๊น์ด ๋น ์ง(๊ธฐ์ค -{REBASE_DEEP_PCT*100:.0f}% ์ดํ) + ๋ด์ฐ๋ฆฌ๊ฐ ์ง์ ์ฐ๋ณด๋ค {(_pg*100 if _pg is not None else 0):+.1f}%({_whyR}). ์์๋ ์๋ / ์ ์์์ {str(dip_dt)[:10]}(60์ {dip_ma60:,.0f}) / ์ ์๋ช ์ {life:,.0f}')) | |
| phase='PEAK'; peak_gap=gap; peak_m60=m60; vlife=None; vbreak_dt=None; vseg_i=len(buys) # โ ์ ์ฐ ๊ทธ๋ฆฌ๊ธฐ ์์ โ ์ ๊ฐ์์๋ช ์ ํ๊ธฐ | |
| endlow_gap=0.0; endlow_ma60=HI; endlow_px=HI | |
| hold=False; wave=0; armed=False; b240={}; flag240up=False | |
| daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| mtn_count=0; life_broken=False # ๊ตฌ์กฐ ์ ์์ โ ๋ค์ ๋์ฐ์ฑ๊ณต์ด 1๋ฒ์งธ | |
| wave_confirmed=False; conf_logged=False; touch_logged=False; cross_up_dt=None; wave_hat=0.0 | |
| prev_mtn_peak=p_now; wave_peak_ma60=0.0 | |
| elif _deep: | |
| # โ ๊น์ ๋๋ฆผ์ธ๋ฐ ํผํฌ๋ ์ ์๋ฆฌ/ํ๋ฝ(์์ ๋ด์ฐ๋ฆฌ) โ ๋์ฐ์ผ๋ก ์ ์นจ(ํธ๋ฆฌํ ์๋). | |
| # ํ์ง๋ง ์ฒซ ๋์ฐ ์๋ช ์ ์ ์ ๊นผ์ผ๋ ์ํ์ค(๋์ฐ+๋๋ธ๋์ฐ)๋ ์ด์์์ (M์๋ ๋๋ธ๋์ฐ). | |
| # 60>240 ๋ณต๊ท = ์ ๋งค์๊ตฌ๊ฐ โ 1๋ฒ์งธ ์์๋๋ถํฐ ์ฌ๊ฐ. ์๋ช ์ ์ ๊ณ์ ์ฒซ ๋์ฐ ๊ฒ. | |
| # (์๋ช ์ ์ด ์ ์ผํ ์ฌ๋ง์กฐ๊ฑด โ ๊นจ์ง๋ฉด DIP ์ค์ ์ด๋ฏธ ์ ๋๋งค๋๋์ ๊ฒ) | |
| _why3 = ('๊ณ๋จ์ ํ๋ฝ' if (_pg is not None and _pg < DOUBLE_MTN_GAP_MIN) | |
| else ('๋์ง์ ์ด ์ง์ ๋ณด๋ค ๋ฎ์์ง' if not _end_ok else 'ํผํฌ๋ฐ์ดํฐ ์์')) | |
| wave_fresh=False # ์ํ์ค ์งํ ์ค(์ฌ๊ฐ) โ ๋ชจ์ ๋ฌด์ | |
| # โ ์ฌ๊ฐ ๋๋ ์๋ช ์ ์ฌ๋ ค์ก๊ธฐ (์ง์ ๋๋ฆผ ์ต์ BBํ๋จ). ๋ด๋ฆฌ์ง๋ ์์. | |
| _oldlife3 = life | |
| if daily and VIRTUAL_LIFELINE and dip_bbmin < HI: | |
| _v3 = dip_bbmin*(1-LIFELINE_MARGIN) | |
| vlife = _v3 if (life is None or _v3 > life) else None | |
| vseg_i = len(buys); vbreak_dt=None | |
| one_wave = bool(DEEP_DIP_ONE_WAVE and dip_gap <= -DEEP_DIP_ONE_WAVE_PCT) | |
| if DOUBLE_MTN_RELIFE and dip_bbmin < HI: | |
| _c3 = dip_bbmin*(1-LIFELINE_MARGIN) | |
| if life is None or _c3 > life: life = _c3 | |
| ev.append((d,'์์๋ด์ฐ๋ฆฌ(๋์ฐ์๋)โ์์๋ ์ฌ๊ฐ', | |
| f'์ด๋ฒ 60์ ๊ผญ๋๊ธฐ {(p_now if p_now else 0):,.0f} vs ์ง์ ์ฐ ๊ผญ๋๊ธฐ {(p_prev if p_prev else 0):,.0f} = {(_pg*100 if _pg is not None else 0):+.1f}% ' | |
| f'(๊ผญ๋๊ธฐ๊ฐ ์ง์ ๊ณผ ๋น์ทํด์ผ[-{abs(DOUBLE_MTN_GAP_MIN)*100:.0f}%~+{DOUBLE_MTN_GAP*100:.0f}%] ๋๋ธ๋์ฐ์ธ๋ฐ ๋ฒ์ด๋จ โ {_why3}) / ' | |
| f'60์ ์ด 240์ ๋ฐ์ผ๋ก {dip_gap*100:.1f}% ๋น ์ง(์ฌ๋ฒ ์ด์ค ๊ธฐ์ค -{REBASE_DEEP_PCT*100:.0f}% ๋ฏธ๋ฌ์ด๋ผ ๋์ฐ ๋ค์ ์ ๊ทธ๋ฆผ) ' | |
| f'โ ์ ์ฐ ์๋(๋์ฐ ๊ฐ์ ์ ์ฌ๋ฆผ). ์๋ช ์ {life:,.0f}' | |
| + (f'(์ง์ ๋๋ฆผ ์ต์ BB๋ก ์ฌ๋ฆผ, ๊ธฐ์กด {_oldlife3:,.0f})' if (_oldlife3 and life>_oldlife3) else '') | |
| + f' โ ์์๋ ๋งค์๋ง 1๋ฒ์งธ๋ถํฐ ๋ค์')) | |
| phase='WAVE'; wave=0; armed=False; b240={}; flag240up=False | |
| lwl=None; lwl_c=None; daily_open=False; pend=[]; up_peak=0.0; up_days=0 | |
| wave_confirmed=False; conf_logged=False; touch_logged=False; cross_up_dt=None; wave_hat=0.0 # ๋ณต๊ทํ์ธ ๋ฆฌ์ | |
| prev_end60=dip_ma60 # ๋ฎ์์ง ๋์ง์ ์ด ์ ๋น๊ต ๊ธฐ์ค (๋์ฐ ์นด์ดํธ๋ ๊ทธ๋๋ก) | |
| prev_mtn_peak=p_now; wave_peak_ma60=0.0 | |
| else: | |
| # ์ฌ๊ธฐ ๋๋ฌ = _deep์ด False (๋๊น์ด -7% ๋ฏธ๋ฌ). ์ฆ ์ง์ง ๋์ง์ ์ด ์๋๋ผ '์์ ๋๋ฆผ'. | |
| # ํผํฌ๊ฐญ์ด ๋ฐด๋ ์์ด์ด๋ ๋์ง์ ์ด ์์ผ๋ฉด ๋์ฐ ์๊ฒฉ ๋ฏธ๋ฌ โ ๊ทธ๋ฅ ๋๋ฆผ(์์๋ ๊ณ์) | |
| gp = _pg*100 if _pg is not None else 0.0 | |
| wave_fresh=False # ์ํ์ค ์งํ ์ค(์์๋๋ฆผ, ์์๋ ๊ณ์) โ ๋ชจ์ ๋ฌด์ | |
| ev.append((d,'๋๋ธ๋์ฐ ์๋โ์์๋ ๊ณ์', | |
| f'๋๊น์ด {dip_gap*100:.2f}% > -{emin*100:.0f}% (์์ ๋๋ฆผ, ์ง์ง ๋์ง์ ์๋) / ํผํฌ๊ฐญ {gp:+.1f}% โ ์์๋ ๊ณ์')) | |
| phase='WAVE' | |
| prev_mtn_peak=p_now; wave_peak_ma60=0.0 | |
| up_peak=0.0; up_days=0 | |
| prev_above=above | |
| if sf: | |
| ev=[e for e in ev if str(e[0])>=sf]; buys=[x for x in buys if str(x[0])>=sf] | |
| mts=[m for m in mts if str(m['start'])>=sf] | |
| # โ ๋ง์ง๋ง ๋ด ๊ธฐ์ค '์ง๊ธ ์ํ' ํ ์ค ์์ฝ (full_debug/app์ด be.LAST_STATUS๋ก ์ฝ์). ๋ฐํ๊ฐ์ ์ ๋ฐ๊ฟ. | |
| # LAST_STATUS_KIND: ์์น ์ฉ ์ข ๋ฅ โ 'buy'(๋งค์๊ตญ๋ฉด,๋นจ๊ฐ) / 'danger'(๋ฌด๋์ง๋์ค,์ฃผํฉ) / 'dip'(๋๋ฆผํ์ ) | |
| # / 'done'(๋งค์๋) / 'wait'(๊ด๋ง) | |
| global LAST_STATUS, LAST_STATUS_KIND, LAST_STATUS_SHORT, LAST_STATUS_DETAIL | |
| try: | |
| _g = f"{gap*100:+.1f}%" if 'gap' in dir() else '' | |
| _gap_txt = f"60์ ์ด 240์ ๋ณด๋ค {_g}" if _g else '' | |
| # ๋์ฐ ์ด๋ฆ(๋ช ๋ฒ์งธ ์ฐ์ธ์ง)์ ์ฝ๊ฒ | |
| _mtn_nm = {1:'์ฒซ ์ฐ', 2:'๋๋ธ์ฐ(2๋ฒ์งธ)', 3:'ํธ๋ฆฌํ์ฐ(3๋ฒ์งธ)'}.get(mtn_count, f'{mtn_count}๋ฒ์งธ ์ฐ') | |
| # โ '์ด๋ค ๋์ฐ โ ์์๋ ๋ช ๋ฒ' ์คํ ๋ฆฌ ์๋จธ๋ฆฌ (ํ์ฌ ์ด์์๋ ์ฐ = mts[-1]) | |
| _mtn = mts[-1] if mts else None | |
| if _mtn: | |
| _sd = str(_mtn['sdt'])[:10]; _ed = str(_mtn['edt'])[:10] | |
| if _sd == _ed: | |
| # ๋๋ธ/ํธ๋ฆฌํ๋์ฐ: ์์๋ฐ๋ฅ=๋์ง์ (2) ๊ฐ์ ๋ (๋ณ๋ ์์๋ฐ๋ฅ ์์) โ ํ์ ์๋ฆฌ๋ก ํํ | |
| _sanhead = f"{_ed} ์๋ฆฌ์์ ํ์ ๋ {_mtn_nm}" | |
| else: | |
| _sanhead = f"{_sd} ์์ํ {_mtn_nm}(๋์ง์ {_ed})" | |
| else: | |
| _sanhead = _mtn_nm | |
| _wtxt = f"์์๋ {wave}๋ฒ์งธ๊น์ง ๋งค์ํจ" if wave else "์์ง ์์๋ ๋งค์ ์ " | |
| if phase=='WAIT': | |
| LAST_STATUS_SHORT = '๊ด๋ง' | |
| LAST_STATUS_DETAIL = '์์ง ์ฐ ์์ ์ ํจ (60์ ์ด 240์ ์๋์์ ๋ฐ๋ฅ ๋ค์ง๋ ์ค)' | |
| LAST_STATUS_KIND = 'wait' | |
| elif phase=='PEAK': | |
| _pk_detail = f"์์ ๋ฐ๋ฅ {str(mtn_sdt)[:10]}(60์ {mtn_s60:,.0f}), ๋ด์ฐ๋ฆฌ ์ต๊ณ +{peak_gap*100:.1f}% / ์๋ช ์ {life:,.0f}" | |
| if life_broken: | |
| LAST_STATUS_SHORT = '๋ฌด๋์ง๋ ์ค' | |
| LAST_STATUS_DETAIL = f"์๋ช ์ {life:,.0f} ์๋๋ก ๋ด๋ ค๊ฐ(์์ ๋จ). 60์ ์ด ์์๋ณด๋ค ๋๊ฒ ๋๋๋ฉด ๊ทธ๋ ๋ค์ ์ฑ๊ณต ์ธ์ " | |
| LAST_STATUS_KIND = 'danger' | |
| elif above: | |
| LAST_STATUS_SHORT = f'{str(mtn_sdt)[:10]} ์์ํ๋ ๋์ฐ ๊ทธ๋ฆฌ๋ ์ค' | |
| LAST_STATUS_DETAIL = _pk_detail | |
| LAST_STATUS_KIND = 'wait' | |
| else: | |
| LAST_STATUS_SHORT = f'{str(mtn_sdt)[:10]} ์์ํ๋ ๋์ฐ ํ ๋์ง์ ๊ฐ๋ ์ค' | |
| LAST_STATUS_DETAIL = _pk_detail | |
| LAST_STATUS_KIND = 'wait' | |
| elif phase=='DIP': | |
| _durd = (idx[-1]-dip_start_dt).days if dip_start_dt is not None else 0 | |
| LAST_STATUS_SHORT = '๋๋ฆผ ํ์ (๋งค์๋ฉ์ถค)' | |
| LAST_STATUS_DETAIL = (f"{_sanhead}์์ {_wtxt}. ๊ทธ ๋ค 60์ ์ด 240์ ์๋๋ก ๊บพ์ฌ {str(dip_start_dt)[:10]}๋ถํฐ " | |
| f"{_durd}์ผ์งธ ๋๋ฆผ ํ์ ์ค(์ง๊ธ 60์ ์ด 240์ ๋ณด๋ค {dip_gap*100:.1f}%) โ ๋งค์ ๋ฉ์ถ๊ณ " | |
| f"๋๋ธ๋์ฐ์ธ์ง ๋ฌด๋์ง ๊ฑด์ง ์ง์ผ๋ณด๋ ์ค") | |
| LAST_STATUS_KIND = 'dip' | |
| elif phase=='WAVE': | |
| if flag240up: | |
| LAST_STATUS_SHORT = '๋งค์ ๋(์ฌ๋ ์ค)' | |
| LAST_STATUS_DETAIL = (f"{_sanhead}์์ ์์๋ 2๋ฒ ๋ค ๋ง์นจ โ ๋งค์ ๋. " | |
| f"๋ค์ 240์ ์๋๋ก ๊บพ์ผ ๋๊น์ง ์ฌ๋ ์ค ({_gap_txt})") | |
| LAST_STATUS_KIND = 'done' | |
| elif daily_open: | |
| _ud = f"์ง๊ธ์ 60์ ์๋ก ํ({up_days}๋ด์งธ) โ ์ ๊น ๋งค์ ์ฌ๋ ์ค" if up_days else "์ง๊ธ 60์ ๋ฐ โ ๋งค์ ์ค" | |
| LAST_STATUS_SHORT = '๋งค์ ๊ตญ๋ฉด' | |
| LAST_STATUS_DETAIL = f"{_sanhead}์์ {wave}๋ฒ์งธ ์์๋ ๋งค์ ์ค. {_ud} ({_gap_txt})" | |
| LAST_STATUS_KIND = 'buy' | |
| elif wave==0: | |
| if wave_confirmed: | |
| LAST_STATUS_SHORT = '๋งค์ ๊ตญ๋ฉด(๋๊ธฐ)' | |
| LAST_STATUS_DETAIL = f"{_mtn_nm} ์์๋ ์ด๋ฆผ, 1๋ฒ์งธ ๋๋ฆผ ๋๊ธฐ(๋ณต๊ท ํ์ธ๋จ, 60์ ๋ฐ์ผ๋ก ์ค๋ฉด ๋งค์) ({_gap_txt})" | |
| LAST_STATUS_KIND = 'buy' | |
| else: | |
| LAST_STATUS_SHORT = '๋ณต๊ท ํ์ธ ๋๊ธฐ' | |
| LAST_STATUS_DETAIL = f"{_mtn_nm} ์์๋ ์ด๋ ธ์ง๋ง ๊ฐ์ง(๋ชจ์)์ผ ์ ์์ด ์์ง ๋งค์ ์ ({_gap_txt})" | |
| LAST_STATUS_KIND = 'wait' | |
| else: | |
| LAST_STATUS_SHORT = '์ ๊ตฌ๊ฐ ๋๊ธฐ' | |
| LAST_STATUS_DETAIL = f"{_mtn_nm} ยท {wave}๋ฒ์งธ ๋๋ฆผ ํ ์ ๊ตฌ๊ฐ ๋๊ธฐ ์ค ({_gap_txt})" | |
| LAST_STATUS_KIND = 'wait' | |
| else: | |
| LAST_STATUS_SHORT = phase; LAST_STATUS_DETAIL = ''; LAST_STATUS_KIND = 'wait' | |
| LAST_STATUS = f"{LAST_STATUS_SHORT} โ {LAST_STATUS_DETAIL}" if LAST_STATUS_DETAIL else LAST_STATUS_SHORT | |
| except Exception as _e: | |
| LAST_STATUS = f"(์ํ ์์ฝ ์คํจ: {_e})"; LAST_STATUS_KIND = 'wait'; LAST_STATUS_SHORT=''; LAST_STATUS_DETAIL='' | |
| return ev,buys,mts | |
| if __name__=='__main__': | |
| path=sys.argv[1] if len(sys.argv)>1 else '007340_5min.csv' | |
| # ํ์ํ๋ ์: ์ธ์๋ก ์ฃผ๋ฉด ๊ทธ๊ฑฐ, ์์ผ๋ฉด ํ์ผ๋ช ์์ ์๋๊ฐ์ง | |
| if len(sys.argv)>2: | |
| tf=sys.argv[2] | |
| else: | |
| pl = path.lower() | |
| if '1day' in pl or '_day' in pl or 'daily' in pl: tf='1day' | |
| elif '1hour' in pl or '60min' in pl or '1h' in pl: tf='1hour' | |
| elif '15min' in pl: tf='15min' | |
| else: tf='5min' | |
| print(f"(ํ์ํ๋ ์ ์๋๊ฐ์ง: {tf})") | |
| sf=sys.argv[3] if len(sys.argv)>3 else None | |
| df=pd.read_csv(path,parse_dates=['datetime'],index_col='datetime') | |
| ev,buys,mts=run(df,tf,sf) | |
| print(f"=== {path} ({tf}) | {df.index[0]} ~ {df.index[-1]} ===\n") | |
| print(f"[๋์ฐ ์ฑ๊ณต {len(mts)}๊ฐ]") | |
| for m in mts: | |
| print(f" {m['start']} | ์์์ 60์ {m['s60']:,.0f} | ๋์ง์ 60์ {m['e60']:,.0f} | ์๋ช ์ {m['life']:,.0f}") | |
| print(f"\n[์ด๋ฒคํธ]") | |
| for d,t,dt in ev: print(f" {str(d):<20}{t:<24}{dt}") | |
| print(f"\n[๋งค์ {len(buys)}๊ฐ]") | |
| tot=0 | |
| for d,p,a2,lb in buys: print(f" {str(d):<20}{p:>8,.0f}์ {a2:>9,}์ {lb}"); tot+=a2 | |
| print(f" ์ด {tot:,}์") |