스톡핑 · 매수 후 최고가 기준 매매 알림

작품 설명
보유 종목의 평단가와 "내가 산 뒤 최고가"를 기준으로, 정한 만큼 떨어지면 팔지 더 살지를 판단해 휴대폰 알림으로 알려주는 앱입니다. 국내·미국 주식을 함께 다루고, 한 종목을 여러 번 나눠 샀다면 매수 차수를 각각 기록해 나란히 비교합니다. React Native(Expo) 앱 하나와 파이썬 서버 하나로 구성했고, 서버는 토스증권 Open API로 시세와 일봉을 받아 매수일 이후 최고가와 판단 단계만 계산합니다. 평단가·수량·손익은 서버로 보내지 않고 기기 안에만 둡니다.
기획 의도
증권 앱의 가격 알림은 "얼마 이하"만 걸 수 있어서, 정작 알고 싶은 "내가 산 뒤 고점에서 몇 % 빠졌는지"는 매번 차트를 되짚어야 했습니다. 판단 기준이 머릿속에만 있으니 매번 흔들리고, 기준에 닿는 순간은 장중에 계속 볼 수 없어 놓쳤습니다. 그래서 판단 축을 "매수 후 최고가 대비 하락률" 하나로 통일하고, 기준에 닿는 순간 알림이 오게 만들었습니다. "시장이 통째로 빠진 것"과 "그 회사만 망가진 것"을 가르기 위해 지수 동반 하락이라는 축을 하나 더 뒀습니다. 사용자는 토큰 발급 같은 것을 하지 않습니다. 설치하고 종목·매수가·매수일만 넣으면 끝입니다.
주요 기능
판단 사다리 — 계속보유 · 관망 · 매도준비 · 매도결정
∙
매수 후 최고가(장중 고가) 대비 하락률로 판정. 기본 -10 / -20 / -30%이고 전체 기본값과 종목별 값을 따로 둘 수 있음
∙
평단가 대비 -5% 추가매수 검토, -20% 손절선은 판단 단계를 바꾸지 않는 보조 신호로 함께 표시
∙
종목 카드에 현재가·평가액·평단 대비 수익률·매수 후 최고가와 날짜·고점 대비 하락률·판단 배지를 한 번에


매수 차수 비교
∙
한 종목에 매수가·수량·매수일이 다른 차수를 몇 개든 추가. 평단가는 수량 가중으로 계산
∙
차수별 현재가 대비 수익률·평가손익·비중과 전체 평단 합계, "지금 가격에 더 사면 평단이 얼마가 되는지"를 상세 화면에서 비교
∙
매수일은 손으로 치지 않고 연·월·일 바퀴로 고르게 해 날짜 표기가 섞여 서버가 못 읽는 일을 차단




한글 종목 검색
∙
삼성전자 · 005930 · 애플 · AAPL처럼 한글 종목명·코드·티커를 한 검색창에서 자동완성
∙
코스피·코스닥·나스닥·뉴욕 시장을 응답의 시장 코드로 판별해 통화와 비교 지수를 자동으로 맞춤

알림 — 순간 알림과 정기 요약
∙
판단 단계가 바뀌는 순간에만 서버가 Expo 푸시로 한 통. 같은 종목은 몇 명이 보든 시세는 한 번만 받고 알림은 등록한 기기에만
∙
원하는 시각을 여러 개 지정하면 그 시각마다 보유 종목 현황을 한 통으로 요약. 장 시작·마감 알림은 서머타임을 반영해 미국장도 한국 시간으로 예약
∙
알림이 종목마다 따로 와서 하루 24통이 쌓이던 문제를 '정한 시각엔 요약 한 통, 오락가락은 한 번만'으로 정리


지수 동반 하락 알림
∙
매수일 종가 기준으로 지수(코스피·코스닥·나스닥 종합·S&P500)와 내 종목을 나란히 재서, 지수가 30% 넘게 빠졌고 내 종목도 ±5%p 안에서 같이 빠졌을 때만 "비중 축소 검토"
∙
종목만 크게 빠진 경우는 시장 탓이 아니므로 알리지 않음. ETF는 지수 비교에서 제외
설계에서 지킨 원칙
확인되지 않은 값은 추정하지 않는다
∙
매수일을 넣은 종목만 그날 이후 일봉에서 최고가를 확인하고, 못 구하면 하락률과 판단을 만들지 않고 "확인 불가"로 둠
∙
장중 고가와 종가 기준 최고가는 값도 날짜도 달라서 둘을 각각 일자와 함께 표시. 판단은 장중 고가 기준
∙
최근 100건 최고가로 대신하던 버그를 겪은 뒤, 매수일보다 앞선 캔들을 만날 때까지 페이지를 거슬러 올라가 계산하도록 고침
평단가는 서버로 올라가지 않는다
∙
토스 Open API가 Client Credentials만 지원해 사용자별 계좌 연동이 불가능하므로, 서버는 계좌와 무관한 시세·캔들 API 22개만 호출
∙
앱 → 서버로는 종목 코드·매수일·기준값·푸시 토큰만 가고, 평단가·수량·손익은 AsyncStorage에만 저장
∙
기기 식별은 Expo 푸시 토큰 하나로 끝나 사용자가 발급받거나 입력할 것이 없음. 저장 파일명에는 토큰 대신 해시
판단 규칙의 단일 진실 출처
∙
같은 판단 사다리가 Python(서버)과 JS(앱·웹)에 있어, 정답표 하나(spec/ladder-cases.json)를 두 언어의 테스트가 함께 읽게 함
∙
기준값을 바꾸면 두 테스트가 동시에 깨져 어느 구현이 안 따라왔는지 드러남. JS 사본은 스크립트로만 생성
인프라와 배포
월 5달러 서버
∙
AWS Lightsail nano(0.5GB)에 파이썬 표준 라이브러리 + websockets 기반 서버를 systemd 서비스로 올리고 Caddy로 TLS 자동화
∙
서비스 둘 합쳐 44MB. 스왑 1G·자동 업데이트 메모리 상한·5분 워치독으로 OOM 재발을 막고, 감시 프로세스는 연속 실패 시 스스로 종료해 systemd가 다시 띄움
∙
토스 자격증명은 AWS SSM SecureString에 두고 SSH 표준입력으로만 흘려 서버에 AWS 자격증명을 남기지 않음
빌드와 배포 경로
∙
EAS 크레딧을 아끼려고 prebuild 후 Xcode·Gradle로 직접 빌드. 스토어 제출만 EAS testflight 프로파일
∙
소개 페이지는 Firebase Hosting, Android APK는 서버의 /dl/ 경로에서 버전 이름을 붙여 배포
∙
index.js가 App 로드를 try/catch로 감싸 릴리스 빌드의 JS 오류를 종료 대신 화면에 띄우고, 네이티브 모듈 누락 빌드를 스크립트로 사전에 차단
인상 깊었던 트러블슈팅
TestFlight 빌드가 켜자마자 죽던 문제
∙
릴리스 빌드에서만 "main has not been registered"로 종료. import가 끌어올려져 try/catch 밖에서 실행되는 바람에 진짜 원인이 사라지고 있었음
∙
App을 require로 바꿔 로드 오류를 잡고, 어떤 경우에도 'main'을 등록해 원인이 화면에 남게 수정. 원인은 ExpoUpdates 네이티브 모듈 누락으로 확정
∙
재발 방지로 빌드 전 네이티브 링크 점검 스크립트를 추가하고 알림·업데이트 모듈은 앱이 켜지는 길목에서 빼 지연 로드
알림이 종목마다 24통 오던 문제
∙
정기 요약과 순간 알림이 겹쳐 종목 수만큼 알림이 쌓임. 시각을 하나라도 정하면 순간 알림은 멈추고 요약 한 통만 오도록 규칙을 바꿈
∙
단계가 오락가락할 때는 바뀐 순간 한 번만 알리고, 알림 발송 오류는 격리해 감시 전체가 죽지 않게 함