此策略優點:高時框POC配合低時框RSI,既讀懂短線市場氛圍也明白長線支撐點位,兩者相互配合的看多策略。
一、前言
在市場中,我們常用「成交量」與「技術指標」作為判斷價格行為的重要依據。本策略將 POC (Point of Control) 的概念與 RSI (Relative Strength Index) 相結合,針對「過去一段時間的成交量分佈」以及「價格相對強弱」進行量化評分,最終以該分數來決定是否買入、賣出,並搭配分數高低動態調整倉位大小。
POC (Point of Control):常見於 Volume Profile 分析,代表特定時間區段內,成交量最多的價格區域。若價格接近高成交量區,往往成為市場的重要支撐或壓力。
RSI (Relative Strength Index):用於評估價格漲跌幅度比例,通常數值偏高 (超買) 代表可能漲幅過大,偏低 (超賣) 代表市場拋售過度。
本策略的核心精神是:
在 RSI 偏低、價格接近支撐(POC) 區域時反向做多;在 RSI 偏高、價格遠離支撐(POC) 區域且呈現 FOMO 狀態時逢高賣出。
利用這種結合成交量與相對強弱的量化打分機制,輔助我們做更加客觀、彈性的資金配置。
二、程式主要邏輯
🔶1. POC (成交量分桶) 計算
回溯 lookback 天 (預設 252 日) 的歷史走勢,找出最高價與最低價,並將整個價格區間平均切成 bin_count 個「桶」 (預設 10)。
逐根 bar 累計該段期間內的成交量 (volume) ,對應到價格所在的桶子;最後根據累計的成交量多寡進行排序。
轉換成 POC 分數:最靠前 (量最多) 的區域排名=1 → 分數=10;最末的排名=10 → 分數=1。
範例:若我們有 10 個桶子 (bin_count=10),在lookback 天 (預設 252 日),最高價是500元,最低價是100元,則每桶的width為(500-100)/10=40元,且假設桶1成交量最高而桶10成交量最低。
則:
桶1(100~140) VOL:10B(排名1)->10分
桶2(140~180) VOL:9B (排名2)->9分
桶3(180~220) VOL:8B (排名3)->8分
.
.
桶10(460~500) VOL:1B(排名10)->1分
當收盤價落在某桶子,程式就能得知該桶子的排名與對應的 POC 分數。
舉例:若收盤價落在215元,可知排名3,可得8分。
🔶2. RSI 計算與分段
使用 rsi_length (預設14) 做 RSI 指標,並將 RSI(0~100) 切成 10 個區間 (bin=1~10)。
區間編號越小 (bin=1),分數越高 (10 分);越大 (bin=10),分數越低 (1 分)。
下表示意 RSI 值與對應分數:
RSI=1~10% (排名1)->10分
RSI=10~20%(排名2)->9分
RSI=20~30%(排名3)->8分
.
.
RSI=90~100%(排名10)->1分
舉例:若 RSI=16% → bin=2 → RSI 分數=9 (偏超賣區域)。
🔶3. 買賣分數 (buy_sell_score)
加權相加:將 POC 分數、RSI 分數分別乘上 poc_weight、rsi_weight,再相加得出最終分數。
範例:若 poc_score=8、rsi_score=9、權重皆為 0.5 →
buy_sell_score=8×0.5+9×0.5=4+4.5=8.5
判斷邏輯:
當「前一根分數 ≥ 7」(含 7) → 進入買訊
當「前一根分數 ≤ 4」(含 4) → 進入賣訊
若分數為小數 (例如 8.5) → floor(8.5)=8 取整數對應下一步買賣比例。
🔶4. 動態倉位大小
當偵測到買訊 (7~10 分),可對應不同買入比例 (INPUT中有最小&最大買入% 供自行調整);
假設: buy_sell score=8分,最小買入=35% , 最大買入=50%
7分=買入35%
8分=35%+(50%-35%)/3=40%
9分=40%+(50%-35%)/3=45%
10分=買入50%
因此:
8分會買入可用資金*40%的部位。
當偵測到賣訊 (4~1 分),也依類似的邏輯進行減倉。
不同之處在於,賣出是用持有部位*賣出% (買入是用可用資金*買入%)
偵測到相對強 (或弱) 的分數 → 部分加碼 (或減碼),讓操作更具有彈性。
🔶5. 可用現金 < 10% 總資產 → 不買
若可用現金 (未投入部位的資金) 已少於總資產的 10%,代表資金相對不足、再次開倉意義不大,可能只會造成小額頻繁交易。此時程式會暫停買入,保留一點資金彈性。
🔶6. 持倉市值 < 10% 總資產 → 不賣
同理,若持倉市值已降至總資產的 10% 以下,意味著部位非常小,賣出再細分也無太大意義,反而浪費交易成本,於是程式也會暫停此部分減倉動作。
🔶7. 下單時機 (收盤)
strategy(..., process_orders_on_close=true):在偵測到訊號當根 K 線收盤即執行下單,不等待下一根開盤。
預設看 buy_sell_score[1] (前一根分數) → 意味著當前根收盤的訊號其實來自「上一根」的計算值;若想更即時,可移除 [1] 改為當根計算當根下單。
🔶8. 圖表顯示:橫向色帶
在副圖中繪製 buy_sell_score 曲線,並搭配 hline() + fill() 將分數 1~4 區塊填充紅色、7~10 區塊填充藍色,便於快速辨識「超買 / 超賣」區域。
🔶9. 可指定回測範圍
若需測試特定區間,程式可透過輸入「開始 / 結束日期」,或使用 testDays 方式(最近 N 天)來限制回測區段,針對短期行情進行檢驗。
三、使用方式
🔶1.套用策略
在 TradingView 「Pine Editor」貼上本策略程式後,儲存並點擊「Add to chart」。
進入「Strategy Tester」檢視回測結果、盈虧曲線、交易訊號等。
🔶2.調整參數
lookback:POC 回溯天數 (預設 252)
bin_count:價格分桶數量 (預設 10)
rsi_length:RSI 週期 (預設 14)
poc_weight / rsi_weight:分數權重 (預設 0.5/0.5)
動態倉位範圍 (最小 / 最大 買入、賣出佔比%)
回測區間 (開始 / 結束日期 或 testDays)
可依交易標的 (如股票、期貨或加密貨幣) 適度調整,並在「Strategy Tester」測試多組組合參數。
🔶3.觀察實際執行
RSI 偏低 & 接近POC支撐 → 於收盤買入
RSI 偏高 & 價格遠離支撐 → 於收盤賣出
;如有小數則取整數判斷。
🔶4.資金控管
可用現金 < 10% → 不買:防止資金見底後反覆小額交易。
持倉市值 < 10% → 不賣:部位過小時,賣出意義不大。
若需更細膩的停損、停利或風控,可在程式內進一步擴充。
四、結語
「POC+RSI Ranking Strategy 完整修正版」結合了成交量分布 (POC) 與 RSI 的打分機制:
RSI 偏低 + 處於成交量聚集區 → 高分 (市場具支撐、偏超賣) → 策略選擇買入;
RSI 偏高 + 價格遠離支撐 → 低分 (市場過熱、偏超買) → 策略逢高賣出。
並透過「動態倉位」讓分數越高 (或越低) 時有相應的加碼(或減碼)佔比,同時做簡易資金控管(小資金不再買、小倉位不再賣)。這些設定配合回測區間(開始/結束日期)使策略更具彈性與實用性。
五、Tradingview程式碼
//@version=5
strategy("POC+RSI Ranking Strategy_V1_AutoDate", overlay=false, process_orders_on_close=true,
initial_capital=1000000,
default_qty_type=strategy.percent_of_equity,
default_qty_value=0)
//------------------------------------------------------
// 1) 參數
//------------------------------------------------------
lookback = input.int(252, "POC回溯天數(約一年)")
bin_count = input.int(10, "分桶數")
rsi_length = input.int(14, "RSI週期")
rsi_weight = input.float(0.5,"RSI 權重(0~1)")
poc_weight = 1 - rsi_weight
// RSI 分數(線性插值: 80→0, 20→10)
useCustomStartDate = input.bool(false, "✅ 啟用自訂回測起始日")
startYear = input.int(2025, "開始年份", inline="start", tooltip="若上方有打勾,才會生效")
startMonth = input.int(1, "月份", inline="start")
startDay = input.int(1, "日期", inline="start")
defaultStart = timestamp(year(timenow) - 1, month(timenow), dayofmonth(timenow), 0, 0)
f_inRange() =>
startTime = useCustomStartDate ? timestamp(startYear, startMonth, startDay, 0, 0) : defaultStart
time >= startTime
//------------------------------------------------------
// 動態「最小 / 最大」買入&賣出百分比
//------------------------------------------------------
minBuyInput = input.float(0.40, "最低買入(%)", minval=0, maxval=1)
maxBuyInput = input.float(0.60, "最多買入(%)", minval=0, maxval=1)
minSellInput = input.float(0.1, "最低賣出(%)", minval=0, maxval=1)
maxSellInput = input.float(0.1, "最多賣出(%)", minval=0, maxval=1)
//------------------------------------------------------
// 2) 基礎計算 (POC)
//------------------------------------------------------
high_year = ta.highest(high, lookback)
low_year = ta.lowest(low, lookback)
price_range = high_year - low_year
bin_size = price_range / bin_count
var float[] bin_volumes = array.new_float(bin_count, 0.0)
var int[] ranks = array.new_int(bin_count, 0)
var int[] indices = array.new_int(bin_count, 0)
if barstate.isfirst
for i = 0 to bin_count - 1
array.set(bin_volumes, i, 0.0)
array.set(indices, i, i)
if barstate.isconfirmed
for i = 0 to bin_count - 1
array.set(bin_volumes, i, 0.0)
for back_i = 0 to lookback - 1
price = hlc3[back_i]
vol = volume[back_i]
idx = math.floor((price - low_year) / bin_size)
idx := idx >= bin_count ? bin_count - 1 : idx
idx := idx < 0 ? 0 : idx
old_v = array.get(bin_volumes, idx)
array.set(bin_volumes, idx, old_v + vol)
// 氣泡排序 (indices)
for i = 0 to bin_count - 2
for j = 0 to bin_count - i - 2
ia = array.get(indices, j)
ib = array.get(indices, j + 1)
va = array.get(bin_volumes, ia)
vb = array.get(bin_volumes, ib)
if va < vb
array.set(indices, j, ib)
array.set(indices, j + 1, ia)
// 產生排名
for r = 0 to bin_count - 1
_idx = array.get(indices, r)
array.set(ranks, _idx, r + 1)
//------------------------------------------------------
// 3) POC + RSI 分數計算 (RSI 線性插值)
//------------------------------------------------------
f_poc_score(_rank) => 11 - _rank
rsi_val = ta.rsi(close, rsi_length)
rsi_score = rsi_val >= 80 ? 0 : rsi_val <= 20 ? 10 : (80 - rsi_val) / 6.0
poc_idx = math.floor((close - low_year) / bin_size)
poc_idx := poc_idx >= bin_count ? bin_count - 1 : poc_idx
poc_idx := poc_idx < 0 ? 0 : poc_idx
poc_score = f_poc_score(array.get(ranks, poc_idx))
poc_weighted = poc_score * poc_weight
rsi_weighted = rsi_score * rsi_weight
buy_sell_score = poc_weighted + rsi_weighted
plot(buy_sell_score, color=color.lime, linewidth=2, title="買入賣出分數")
//------------------------------------------------------
// 4) 買 / 賣訊號
//------------------------------------------------------
// 買: 分數≥7
buySignal = buy_sell_score[1] >= 7
// 賣: 分數≤3 (原本是≤4)
sellSignal = buy_sell_score[1] <= 3
//------------------------------------------------------
// 5) 買賣比例計算
//------------------------------------------------------
f_buy_pct(_score) =>
s = math.floor(_score)
maxBuyInput <= minBuyInput ? maxBuyInput :
s == 7 ? minBuyInput :
s == 8 ? minBuyInput + (maxBuyInput - minBuyInput)*(1/3) :
s == 9 ? minBuyInput + (maxBuyInput - minBuyInput)*(2/3) :
s == 10 ? maxBuyInput : 0.0
f_sell_pct(_score) =>
s = math.floor(_score)
maxSellInput <= minSellInput ? maxSellInput :
s == 3 ? minSellInput :
s == 2 ? minSellInput + (maxSellInput - minSellInput) * (1/2) :
s == 1 ? maxSellInput :
s == 0 ? maxSellInput : 0.0
//------------------------------------------------------
// 6) 水平區塊背景 (改成紅底3~0)
//------------------------------------------------------
line0 = hline(0, "Line0", color=color.new(color.red, 0))
line3 = hline(3, "Line3", color=color.new(color.red, 0))
line7 = hline(7, "Line7", color=color.new(color.blue, 0))
line10 = hline(10, "Line10", color=color.new(color.blue, 0))
// 紅色區間: 3~0
fill(line3, line0, color=color.new(color.red, 80))
// 藍色區間: 7~10 (維持不變)
fill(line7, line10, color=color.new(color.blue, 80))
//------------------------------------------------------
// 7) 交易邏輯
//------------------------------------------------------
if barstate.isconfirmed and f_inRange()
availableCash = strategy.equity - (strategy.position_size * close)
minCashThreshold = strategy.equity * 0.1
// 買入
if buySignal
if availableCash >= minCashThreshold
buyPct = f_buy_pct(buy_sell_score[1])
if buyPct > 0
tv = strategy.equity * buyPct
tv := tv < 0 ? 0 : tv
sh = tv / close
if sh > 0
strategy.order("Buy_" + str.tostring(buy_sell_score[1]), strategy.long, sh)
// 賣出
if sellSignal and strategy.position_size > 0
holdVal = strategy.position_size * close
if holdVal >= strategy.equity * 0.1
sp = f_sell_pct(buy_sell_score[1])
if sp > 0
ss = strategy.position_size * sp
if ss > 0
strategy.order("Sell_" + str.tostring(buy_sell_score[1]), strategy.short, ss)





















