[NOTICE & LICENSE]
・학습 및 정보 제공만을 목적으로 하며, 투자 조언이 아닙니다. 툴 사용으로 인해 발생한 경제적 손실에 대해 일절 책임을 지지 않습니다.
・Pine Script v6에서 동작을 확인했습니다. 향후 TradingView의 사양 변경에 따른 업데이트를 보장하지 않으며, 개별적인 설치 지원이나 수정 요청은 일절 받지 않습니다 (있는 그대로(As-is) 제공).
・MIT 라이선스가 적용됩니다.
// SPDX-FileCopyrightText: 2026 NK-report https://www.nk-report.com/
// SPDX-License-Identifier: MIT
//
// Disclaimer: This script is for educational purposes only and does not constitute investment advice.
//@version=6
// ※ダイナミックグリッドの描画上限を確保するため、linesとlabelsの上限を拡張しています
indicator("NK-Horizon Round", shorttitle="NK-Horizon", overlay=true, max_lines_count=500, max_labels_count=500)
// ==============================================================================
// 【01】 免責・ライセンス
// ==============================================================================
//
// 지표를 무료로 공개하고 있습니다: https://www.nk-report.com/p/kr-tradingview.html
//
// 면책 조항: 본 스크립트는 학습 및 정보 제공만을 목적으로 하며, 투자 조언이 아닙니다.
//
// 1. 본 코드는 2026년 기준 Pine Script v6에서 동작 확인을 마쳤습니다.
// 향후 사양 변경으로 인한 오류 등에 대해 개별적인 지원이나 수정은 제공하지 않습니다.
// 2. 본 스크립트는 일반적인 계산 로직을 바탕으로 LLM을 활용하여 독자적으로 작성된 것입니다.
// MIT 라이선스를 따릅니다.
// ------------------------------------------------------------------------------
// MIT License
//
// Copyright 2026 NK-report https://www.nk-report.com/
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ==============================================================================
// 【02】 UI設定と言語
// ==============================================================================
const string GRP_GENERAL = "▼ 기본 설정"
const string GRP_MAIN = "▼ 메인 레벨 (라운드 넘버)"
const string GRP_SUB = "▼ 서브 레벨 (중간 라운드 넘버)"
const string OPT_STY_S = "실선"
const string OPT_STY_D = "파선"
const string OPT_STY_DT = "점선"
const string OPT_POS_L = "좌측"
const string OPT_POS_C = "중앙"
const string OPT_POS_R = "우측"
// 基本設定
i_step = input.float(1.0, title="간격 (라운드 넘버 단위)", step=0.1, group=GRP_GENERAL, tooltip="USD/JPY의 경우 1 또는 0.5, 닛케이 지수의 경우 500 또는 100 등 대상 종목의 기준 수치를 지정해 주세요.")
i_num_lines = input.int(10, title="상하 그리기 개수 (단방향)", minval=1, maxval=50, group=GRP_GENERAL, tooltip="현재 가격을 기준으로 위로 X개, 아래로 X개를 그립니다. 기본값(10)인 경우, 총 21개의 선이 현재 가격을 따라다닙니다.")
i_show_lbl = input.bool(true, title="가격 레이블 표시", group=GRP_GENERAL, inline="lbl")
i_lbl_pos = input.string(OPT_POS_R, title="위치", options=[OPT_POS_L, OPT_POS_C, OPT_POS_R], group=GRP_GENERAL, inline="lbl")
i_lbl_offset = input.int(22, title="슬라이드 조정 (봉 개수)", minval=0, group=GRP_GENERAL, tooltip="'좌측' 또는 '우측'을 선택한 경우, 최신 캔들(봉)에서 얼마나 떨어지게 할지 조정합니다.\n※ '중앙'을 선택한 경우 이 수치는 무시됩니다.")
// メインレベル設定
i_show_main = input.bool(true, title="표시", group=GRP_MAIN, inline="main1")
i_col_main = input.color(color.black, title="색상", group=GRP_MAIN, inline="main1")
i_wid_main = input.int(1, title="굵기", minval=1, maxval=5, group=GRP_MAIN, inline="main1")
i_sty_main = input.string(OPT_STY_S, title="스타일", options=[OPT_STY_S, OPT_STY_D, OPT_STY_DT], group=GRP_MAIN, inline="main1")
// サブレベル設定
i_show_sub = input.bool(false, title="표시", group=GRP_SUB, inline="sub1")
i_col_sub = input.color(color.black, title="색상", group=GRP_SUB, inline="sub1")
i_wid_sub = input.int(1, title="굵기", minval=1, maxval=5, group=GRP_SUB, inline="sub1")
i_sty_sub = input.string(OPT_STY_DT, title="스타일", options=[OPT_STY_S, OPT_STY_D, OPT_STY_DT], group=GRP_SUB, inline="sub1")
// ==============================================================================
// 【03】 全コード共通仕様(時間・インフラ処理)
// ==============================================================================
// ※該当コードなし
// (本ツールは時間枠に依存せず、常に現在の価格のみを参照して水平線を引くため、時間の変換処理は不要です)
// ==============================================================================
// 【04】 各カテゴリ共通仕様 (描画インフラと判定ロジック)
// ==============================================================================
f_get_style(sty_str) =>
sty_str == OPT_STY_S ? line.style_solid : sty_str == OPT_STY_D ? line.style_dashed : line.style_dotted
// ==============================================================================
// 【05】 このコード固有の計算仕様 (Specific Indicator Logic)
// ==============================================================================
// エレベーター方式の描画オブジェクトを格納する配列群 (Object Pooling)
var array<line> a_main_l = array.new<line>()
var array<label> a_main_lbl = array.new<label>()
var array<line> a_sub_l = array.new<line>()
var array<label> a_sub_lbl = array.new<label>()
// 初回ロード時:必要な本数分のオブジェクトを1セットだけ生成し、配列に格納・待機させる
if barstate.isfirst
int total_lines = (i_num_lines * 2) + 1
for i = 0 to total_lines - 1
// メインライン用
a_main_l.push(line.new(na, na, na, na, extend=extend.both, color=i_col_main, width=i_wid_main, style=f_get_style(i_sty_main)))
a_main_lbl.push(label.new(na, na, text="", color=color.new(color.white, 100), textcolor=i_col_main, style=label.style_none, size=size.small))
// サブライン用
a_sub_l.push(line.new(na, na, na, na, extend=extend.both, color=i_col_sub, width=i_wid_sub, style=f_get_style(i_sty_sub)))
a_sub_lbl.push(label.new(na, na, text="", color=color.new(color.white, 100), textcolor=i_col_sub, style=label.style_none, size=size.small))
// ==============================================================================
// 【06】 描画と出力 (Rendering & Outputs)
// ==============================================================================
// リアルタイム更新:過去の足では計算せず、最新の足でのみ配列内の座標をスライド更新する
if barstate.islast
// 剰余算による「最も近いラウンドナンバー」の数学的特定
float center_p = math.round(close / i_step) * i_step
int total_lines = (i_num_lines * 2) + 1
// 最新足(最前線)をアンカーにしたラベルX座標の決定
int lbl_x = bar_index
if i_lbl_pos == OPT_POS_L
lbl_x := bar_index - i_lbl_offset
else if i_lbl_pos == OPT_POS_R
lbl_x := bar_index + i_lbl_offset
for i = 0 to total_lines - 1
int offset = i - i_num_lines
float p_main = center_p + (offset * i_step)
// --- メインレベルの更新 ---
line l_m = a_main_l.get(i)
label lbl_m = a_main_lbl.get(i)
if i_show_main
line.set_xy1(l_m, bar_index, p_main)
line.set_xy2(l_m, bar_index + 1, p_main)
line.set_color(l_m, i_col_main)
line.set_width(l_m, i_wid_main)
line.set_style(l_m, f_get_style(i_sty_main))
if i_show_lbl
label.set_xy(lbl_m, lbl_x, p_main)
label.set_text(lbl_m, str.tostring(p_main, format.mintick))
label.set_textcolor(lbl_m, i_col_main)
else
label.set_xy(lbl_m, na, na)
else
line.set_xy1(l_m, na, na)
line.set_xy2(l_m, na, na)
label.set_xy(lbl_m, na, na)
// --- サブレベル (半値) の更新 ---
line l_s = a_sub_l.get(i)
label lbl_s = a_sub_lbl.get(i)
if i_show_sub
float p_sub = p_main + (i_step * 0.5)
line.set_xy1(l_s, bar_index, p_sub)
line.set_xy2(l_s, bar_index + 1, p_sub)
line.set_color(l_s, i_col_sub)
line.set_width(l_s, i_wid_sub)
line.set_style(l_s, f_get_style(i_sty_sub))
if i_show_lbl
label.set_xy(lbl_s, lbl_x, p_sub)
label.set_text(lbl_s, str.tostring(p_sub, format.mintick))
label.set_textcolor(lbl_s, i_col_sub)
else
label.set_xy(lbl_s, na, na)
else
line.set_xy1(l_s, na, na)
line.set_xy2(l_s, na, na)
label.set_xy(lbl_s, na, na)
// ※本ツールは「背景グリッド」としての役割に徹するため、ノイズ防止の観点からデータウィンドウへの出力は行いません。
* 원하는 색상으로 설정한 후, 설정 탭에서 '기본값으로 저장'을 눌러주세요.
* 새로 만들기 -> 지표 -> 붙여넣기 순서로 진행하지 않으면 정상적으로 표시되지 않을 수 있습니다.
