0%

用 Python 模擬「3%訊號線交易」與「熊市保護」


讓你的投資策略動起來:用 Python 模擬「3%訊號線交易」與「熊市保護」


投資的世界總是充滿不確定性,如何在市場波動中保持理性、堅持策略,是許多投資人面臨的挑戰。「再平衡」是一種常見的投資策略,它幫助我們將資產配置維持在目標比例。今天,我們將探討一個更進階的「3%訊號線交易策略」,並用 Python 程式碼來模擬它。更棒的是,我們還會加入關鍵的「跌30%不出場」機制,幫助我們在極端熊市中保持冷靜。


什麼是「3%訊號線交易」?

「3%訊號線交易」是一種資產再平衡策略,其核心在於:

  1. 設定目標資產配置比例: 例如,80% 股票 ETF 和 20% 債券 ETF。
  2. 定期(例如每季)檢視: 計算股票資產相對於目標比例的偏離度
  3. 觸發交易: 只有當實際股票部位與目標比例的偏離度超過 3% 時,才進行再平衡操作(買入或賣出股票/債券,使比例回到目標)。如果偏離度在 3% 以內,則不進行任何交易。

這種策略的優點在於減少不必要的頻繁交易,避免高頻手續費,並讓投資人專注於大趨勢,而非短期波動。


Python 模擬程式碼:初步實踐「3%訊號線交易」

我們可以用 Python 來模擬這個策略,觀察在不同市場報酬率下的資產變化。下面的程式碼範例假設我們已經知道每季股票 ETF 和債券 ETF 的報酬率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
def three_percent_signal_trading(
initial_stock_value,
initial_bond_value,
target_stock_allocation=0.8, # 目標股票配置比例,例如80%
target_bond_allocation=0.2, # 目標債券配置比例,例如20%
signal_threshold=0.03, # 3%訊號線
quarterly_returns_stock=None,
quarterly_returns_bond=None
):
"""
模擬「3%訊號線交易」策略。
Args:
initial_stock_value (float): 初始股票ETF價值。
initial_bond_value (float): 初始債券ETF價值。
target_stock_allocation (float): 目標股票配置比例 (0到1之間)。
target_bond_allocation (float): 目標債券配置比例 (0到1之間)。
signal_threshold (float): 3%訊號線,即觸發交易的波動閾值。
quarterly_returns_stock (list): 每季股票ETF的報酬率列表 (例如: [0.05, -0.02, 0.10])。
quarterly_returns_bond (list): 每季債券ETF的報酬率列表。
Returns:
tuple: 最終股票價值, 最終債券價值, 交易紀錄。
"""
current_stock_value = initial_stock_value
current_bond_value = initial_bond_value
trade_log = []

print(f"--- 初始資產配置 ---")
print(f"初始股票價值: ${current_stock_value:,.2f}")
print(f"初始債券價值: ${current_bond_value:,.2f}")
print(f"總資產: ${current_stock_value + current_bond_value:,.2f}\n")

num_quarters = len(quarterly_returns_stock) if quarterly_returns_stock else 0

for q in range(num_quarters):
print(f"--- 第 {q+1} 季 ---")
# 1. 計算本季資產變化 (應用報酬率)
if quarterly_returns_stock and q < len(quarterly_returns_stock):
current_stock_value *= (1 + quarterly_returns_stock[q])
if quarterly_returns_bond and q < len(quarterly_returns_bond):
current_bond_value *= (1 + quarterly_returns_bond[q])

total_value = current_stock_value + current_bond_value
target_stock_value = total_value * target_stock_allocation

# 2. 判斷是否需要交易 (3%訊號線)
# 檢查股票實際價值與目標價值的差距百分比
deviation = (current_stock_value - target_stock_value) / target_stock_value
trade_amount = 0
trade_type = "無交易"
log_message = ""

if abs(deviation) >= signal_threshold:
# 需要調整的金額
trade_amount = abs(current_stock_value - target_stock_value)
if deviation > 0: # 股票部位過高,賣出股票,買入債券
current_stock_value -= trade_amount
current_bond_value += trade_amount
trade_type = "賣出股票"
log_message = f"股票部位過高 ({deviation*100:.2f}%),賣出股票 ${trade_amount:,.2f},轉入債券。"
else: # 股票部位過低,賣出債券,買入股票
current_stock_value += trade_amount
current_bond_value -= trade_amount
trade_type = "買入股票"
log_message = f"股票部位過低 ({deviation*100:.2f}%),買入股票 ${trade_amount:,.2f},來自債券。"
else:
log_message = f"股票偏離度({deviation*100:.2f}%)在訊號線({signal_threshold*100:.2f}%)內,無需交易。"

trade_log.append({
"quarter": q + 1,
"stock_value_after_return": current_stock_value, # 應用報酬率後的價值
"bond_value_after_return": current_bond_value,
"total_value": total_value,
"target_stock_value": target_stock_value,
"deviation_percentage": deviation,
"trade_type": trade_type,
"trade_amount": trade_amount,
"message": log_message
})

print(f"當前股票價值 (成長後): ${current_stock_value:,.2f}")
print(f"當前債券價值 (成長後): ${current_bond_value:,.2f}")
print(f"本季總資產: ${total_value:,.2f}")
print(f"目標股票價值: ${target_stock_value:,.2f}")
print(log_message)
print(f"交易後股票價值: ${current_stock_value:,.2f}")
print(f"交易後債券價值: ${current_bond_value:,.2f}\n")

print("--- 模擬結束 ---")
print(f"最終股票價值: ${current_stock_value:,.2f}")
print(f"最終債券價值: ${current_bond_value:,.2f}")
print(f"最終總資產: ${current_stock_value + current_bond_value:,.2f}")

return current_stock_value, current_bond_value, trade_log

# --- 範例使用 ---
quarterly_stock_returns = [0.05, -0.02, 0.10, 0.03, -0.07, 0.12, 0.01, -0.03]
quarterly_bond_returns = [0.01, 0.005, 0.008, 0.003, 0.01, 0.002, 0.005, 0.001]

final_stock, final_bond, log = three_percent_signal_trading(
initial_stock_value=80000,
initial_bond_value=20000,
quarterly_returns_stock=quarterly_stock_returns,
quarterly_returns_bond=quarterly_bond_returns
)

程式碼解釋:

  • three_percent_signal_trading 函數接收初始資產、目標比例、訊號線等參數。
  • 每季,程式碼會先根據報酬率更新資產價值,再計算當前股票部位與目標的偏離度
  • 如果偏離度的絕對值達到或超過 signal_threshold (3%),就會執行再平衡交易,將資產比例拉回目標。
  • trade_log 會記錄每季的詳細操作,方便追蹤。

進階實踐:「跌30%不出場」的熊市保護機制

在「3%訊號線交易」中,還有一個極其重要的風險管理規則:「跌30%不出場」。這個規則的目的是在市場大幅下跌(從歷史高點跌幅達 30% 或以上)時,暫停所有的再平衡交易,避免在市場底部恐慌性賣出股票,導致錯失隨後的反彈機會。

以下是加入此機制的 Python 程式碼範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def three_percent_signal_trading_with_30_percent_rule(
initial_stock_value,
initial_bond_value,
target_stock_allocation=0.8,
target_bond_allocation=0.2,
signal_threshold=0.03,
bear_market_threshold=0.30, # 跌30%的閾值
quarterly_returns_stock=None,
quarterly_returns_bond=None
):
"""
模擬「3%訊號線交易」策略,加入「跌30%不出場」規則。
Args:
initial_stock_value (float): 初始股票ETF價值。
initial_bond_value (float): 初始債券ETF價值。
target_stock_allocation (float): 目標股票配置比例 (0到1之間)。
target_bond_allocation (float): 目標債券配置比例 (0到1之間)。
signal_threshold (float): 3%訊號線,即觸發交易的波動閾值。
bear_market_threshold (float): 觸發熊市保護的下跌閾值,例如 0.30 代表跌30%。
quarterly_returns_stock (list): 每季股票ETF的報酬率列表。
quarterly_returns_bond (list): 每季債券ETF的報酬率列表。
Returns:
tuple: 最終股票價值, 最終債券價值, 交易紀錄。
"""
current_stock_value = initial_stock_value
current_bond_value = initial_bond_value
trade_log = []

# 新增變數來追蹤歷史股票淨值高點
historical_max_stock_value = initial_stock_value
# 新增旗標來判斷是否處於熊市保護模式
in_bear_market_protection = False

print(f"--- 初始資產配置 ---")
print(f"初始股票價值: ${current_stock_value:,.2f}")
print(f"初始債券價值: ${current_bond_value:,.2f}")
print(f"總資產: ${current_stock_value + current_bond_value:,.2f}\n")

num_quarters = len(quarterly_returns_stock) if quarterly_returns_stock else 0

for q in range(num_quarters):
print(f"--- 第 {q+1} 季 ---")
# 1. 計算本季資產變化 (應用報酬率)
if quarterly_returns_stock and q < len(quarterly_returns_stock):
current_stock_value *= (1 + quarterly_returns_stock[q])
if quarterly_returns_bond and q < len(quarterly_returns_bond):
current_bond_value *= (1 + quarterly_returns_bond[q])

total_value = current_stock_value + current_bond_value

# 更新歷史股票淨值高點
if current_stock_value > historical_max_stock_value:
historical_max_stock_value = current_stock_value
# 如果股票價值創新高,則退出熊市保護模式 (因為已經回升了)
in_bear_market_protection = False
print(f"股票價值創新高: ${historical_max_stock_value:,.2f},退出熊市保護模式。")

# 檢查是否觸發「跌30%」或是否仍在保護模式中
if historical_max_stock_value > 0: # 避免除以零
current_stock_drawdown = (historical_max_stock_value - current_stock_value) / historical_max_stock_value

if current_stock_drawdown >= bear_market_threshold and not in_bear_market_protection:
in_bear_market_protection = True
print(f"*** 觸發熊市保護!股票從歷史高點(${historical_max_stock_value:,.2f})下跌了 {current_stock_drawdown*100:.2f}%。本季及後續將暫停再平衡交易。 ***")
elif in_bear_market_protection and current_stock_drawdown < bear_market_threshold:
# 如果股票已經從低點回升,且跌幅小於30%,則解除保護
in_bear_market_protection = False
print(f"股票從熊市中回升,跌幅({current_stock_drawdown*100:.2f}%)小於閾值,解除熊市保護。")

trade_amount = 0
trade_type = "無交易"
log_message = ""

if in_bear_market_protection:
log_message = "處於熊市保護模式,本季不進行再平衡交易。"
else:
target_stock_value = total_value * target_stock_allocation
deviation = (current_stock_value - target_stock_value) / target_stock_value

if abs(deviation) >= signal_threshold:
trade_amount = abs(current_stock_value - target_stock_value)
if deviation > 0:
current_stock_value -= trade_amount
current_bond_value += trade_amount
trade_type = "賣出股票"
log_message = f"股票部位過高 ({deviation*100:.2f}%),賣出股票 ${trade_amount:,.2f},轉入債券。"
else:
current_stock_value += trade_amount
current_bond_value -= trade_amount
trade_type = "買入股票"
log_message = f"股票部位過低 ({deviation*100:.2f}%),買入股票 ${trade_amount:,.2f},來自債券。"
else:
log_message = f"股票偏離度({deviation*100:.2f}%)在訊號線({signal_threshold*100:.2f}%)內,無需交易。"

trade_log.append({
"quarter": q + 1,
"stock_value_after_return": current_stock_value,
"bond_value_after_return": current_bond_value,
"total_value": total_value,
"historical_max_stock_value": historical_max_stock_value,
"in_bear_market_protection": in_bear_market_protection,
"trade_type": trade_type,
"trade_amount": trade_amount,
"message": log_message
})

print(f"當前股票價值 (成長後): ${current_stock_value:,.2f}")
print(f"當前債券價值 (成長後): ${current_bond_value:,.2f}")
print(f"本季總資產: ${total_value:,.2f}")
print(f"當前股票歷史高點: ${historical_max_stock_value:,.2f}")
print(f"是否處於熊市保護: {in_bear_market_protection}")
print(log_message)
print(f"交易後股票價值: ${current_stock_value:,.2f}")
print(f"交易後債券價值: ${current_bond_value:,.2f}\n")

print("--- 模擬結束 ---")
print(f"最終股票價值: ${current_stock_value:,.2f}")
print(f"最終債券價值: ${current_bond_value:,.2f}")
print(f"最終總資產: ${current_stock_value + current_bond_value:,.2f}")

return current_stock_value, current_bond_value, trade_log

# --- 範例使用 (包含更多波動以觸發保護機制) ---
quarterly_stock_returns_volatile = [0.05, 0.02, -0.10, -0.15, -0.08, 0.03, 0.12, 0.05, -0.02, 0.07, 0.03]
quarterly_bond_returns_stable = [0.01, 0.005, 0.008, 0.003, 0.01, 0.002, 0.005, 0.001, 0.004, 0.003, 0.002]

final_stock_30, final_bond_30, log_30 = three_percent_signal_trading_with_30_percent_rule(
initial_stock_value=80000,
initial_bond_value=20000,
quarterly_returns_stock=quarterly_stock_returns_volatile,
quarterly_returns_bond=quarterly_bond_returns_stable,
bear_market_threshold=0.30 # 設定跌30%為保護閾值
)

關鍵改動與解釋:

  • historical_max_stock_value 變數: 追蹤股票淨值的歷史最高點,用來計算當前跌幅。
  • in_bear_market_protection 旗標: 一個布林值,標示目前是否處於熊市保護狀態。
  • 觸發與解除條件:
    • 每季會計算當前股票價值距離歷史高點的跌幅。
    • 當跌幅達到 bear_market_threshold(預設 30%)且尚未進入保護模式時,則觸發保護。
    • 如果已處於保護模式中,但股票價值回升,跌幅小於閾值,則解除保護。
  • 交易邏輯的修改: 只有當 in_bear_market_protectionFalse 時,才會執行原有的 3% 訊號線再平衡判斷和交易。這確保了在熊市中不會恐慌性賣出。

「跌30%不出場」的策略意義

這個機制為你的投資策略增加了重要的風險管理層面:

  • 避免恐慌性賣出: 防止你在市場恐慌性下跌時,因再平衡規則而在低點賣出優質資產。
  • 堅定長期持有信念: 鼓勵你在極端悲觀的市場中保持耐心,相信長期市場會回歸價值。
  • 減少不必要的交易摩擦: 在熊市中暫停再平衡,也能減少頻繁交易可能產生的高昂手續費。

如何在真實環境中應用?

這個 Python 程式碼是為了幫助你理解策略邏輯而設計的模擬範例。在實際的投資環境中,你需要考慮以下幾點:

  1. 數據來源: 你需要獲取你所投資的股票 ETF (例如:VOO, SPY, VT) 和債券 ETF (例如:BND, TLT) 的實際季度報酬率數據。這可以透過財經數據提供商的 API(例如:Yahoo Finance API, Quandl, Alpha Vantage 等)來獲取。
  2. 自動交易與券商 API: Python 本身不具備直接連接券商下單的功能。如果你想實現自動化交易,需要使用券商提供的 API(例如:盈透證券 Interactive Brokers、富途牛牛等)來進行程式化下單。這會涉及更複雜的認證、訂單管理和錯誤處理。
  3. 稅務考量: 每次交易都可能產生稅務影響(例如資本利得稅),這在實際操作中是需要考慮的。
  4. 交易成本: 每次買賣都會有手續費,雖然通常不高,但頻繁交易可能會累積成本。

這個模擬程式提供了一個清晰的框架,讓你了解「3%訊號線交易」與「熊市保護」如何在 Python 中協同運作。如果你希望進一步實踐,就需要考慮數據獲取和實際交易平台的整合。