在前一篇測試中我們將需求前置轉譯整合到 meta-prompt 的通用模板中, 本篇旨在使用它來產生網格交易的高約束提示詞與 Python 程式碼, 檢驗模板的通用性與程式的正確性, 進而優化通用模板.
Gemini 對此提示詞給予 92 分高度評價, 表示這證明通用 Meta-Prompt 模板具備高度的可遷移性 (Transferability), 當策略從單向追蹤的 "ATR 移動停損" 切換為多階段加減碼的 "動態網格交易" 時, 架構師依然精準產出了符合五大原則的高約束規格書.
從變數名稱 prev_pos, prev_grid = curr_pos, curr_grid 可知 ChatGPT 成功地生成網格交易語義的變數, 表示通用模板的可遷移性 (視任務不同產生適配之變數). 這份 Prompt 的結構已經非常嚴密, 但在網格業務邏輯的描述上稍微偏向口語, 可能會讓 14B 小模型在實作程式碼時會出現不同解讀, 摘要如下 :
整體來說, ChatGPT 的這份產出驗證了通用模板是成功的, 證明通用模板 Meta-Prompt 不是 ATR 的專用外殼, 而是真正具備跨策略能力的量化狀態機工程藍圖. 即使直接將這份 Prompt 丟給本機 qwen2.5-coder:14b, 它產出的代碼基本上不會有陣列越界或狀態遺失的語法級 Bug, 只需要留意它採用的網格價格更新公式是否符合預期的回測定義.
def dynamic_grid(df: pd.DataFrame, entry_col: str = "entry_signal", grid_pct: float = 0.02,
lot_size: float = 1.0, max_positions: int = 5) -> pd.DataFrame:
required_cols = ['Close', entry_col]
if not all(col in df.columns for col in required_cols):
raise ValueError(f"Missing required columns: {', '.join(required_cols)}")
if not pd.api.types.is_numeric_dtype(df['Close']):
raise ValueError("Close must be a numeric type.")
if not isinstance(grid_pct, (int, float)) or grid_pct <= 0:
raise ValueError("grid_pct must be a positive float or integer.")
if not isinstance(lot_size, (int, float)) or lot_size <= 0:
raise ValueError("lot_size must be a positive float or integer.")
if not isinstance(max_positions, int) or max_positions <= 0 or isinstance(max_positions,
raise ValueError("max_positions must be a positive integer.")
close = df['Close'].values
entry_signal = df[entry_col].values.astype(bool)
grid_level = np.full(n, np.nan)
position = np.zeros(n, dtype=int)
entry_event = np.zeros(n, dtype=bool)
buy_event = np.zeros(n, dtype=bool)
sell_event = np.zeros(n, dtype=bool)
exit_event = np.zeros(n, dtype=bool)
valid_close = np.isfinite(close[t])
valid_entry = entry_signal[t]
if prev_pos == 0 and valid_entry and valid_close:
if curr_pos < max_positions and close[t] < prev_grid * (1 - grid_pct):
elif curr_pos > 0 and close[t] > prev_grid * (1 + grid_pct):
if curr_pos == 0 and prev_pos > 0:
grid_level[t] = curr_grid
df['Grid_Level'] = grid_level
df['Position'] = position
df['Entry_Event'] = entry_event
df['Buy_Event'] = buy_event
df['Sell_Event'] = sell_event
df['Exit_Event'] = exit_event
'Close': [100, 102, 103, 98, 95, 97, 100, 105, 110, 115, 118],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df)
required_cols = ['Close', 'entry_signal', 'Grid_Level', 'Position', 'Entry_Event',
'Buy_Event', 'Sell_Event', 'Exit_Event']
assert all(col in result.columns for col in required_cols)
# Check Grid_Level values
expected_grid_level = [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0,
np.testing.assert_array_equal(result['Grid_Level'], expected_grid_level)
expected_position = [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
np.testing.assert_array_equal(result['Position'], expected_position)
# Check Entry_Event values
expected_entry_event = [True, False, False, False, False, False, False, False, False,
np.testing.assert_array_equal(result['Entry_Event'], expected_entry_event)
expected_buy_event = [False, False, False, False, False, False, False, False, False, False,
np.testing.assert_array_equal(result['Buy_Event'], expected_buy_event)
# Check Sell_Event values
expected_sell_event = [False, False, False, False, False, False, False, False, False,
np.testing.assert_array_equal(result['Sell_Event'], expected_sell_event)
# Check Exit_Event values
expected_exit_event = [False, False, False, True, False, False, False, False, False, False,
np.testing.assert_array_equal(result['Exit_Event'], expected_exit_event)
# Check that the original DataFrame is not modified
assert 'Grid_Level' not in df.columns
df = pd.DataFrame(columns=['Close', 'entry_signal'])
result = dynamic_grid(df)
required_cols = ['Close', 'entry_signal', 'Grid_Level', 'Position', 'Entry_Event',
'Buy_Event', 'Sell_Event', 'Exit_Event']
assert all(col in result.columns for col in required_cols)
'Close': [100, np.nan, 103, 98, np.nan, 97, 100, np.nan, 110, 115, 118],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df)
assert 'Grid_Level' in result.columns
assert 'Position' in result.columns
assert 'Entry_Event' in result.columns
assert 'Buy_Event' in result.columns
assert 'Sell_Event' in result.columns
assert 'Exit_Event' in result.columns
提交給 Gemini 審查, 它認為 Meta-Prompt 通用模板的五大原則發揮了巨大威力, 完全遵守依序輸出協議; 測試的不可變性也防護成功; 時序與型別防禦也毫無死角. 不過, 此程式碼存在三個硬傷與邏輯缺陷, 摘要如下 :
從此例可知, 我們的 Meta-Prompt 通用模板需要進一步優化,要求架構師將純量暫存器的統一提交出口寫成明確的賦值語句清單而非概括性字眼 (因為對 14B 小模型而言, "全部輸出" 是個高度抽象的集合名詞容易漏抄), 這樣才能封死小模型漏賦值問題.
主要修正之處為通用模板第 4 條約束中的 "統一提交出口", 要禁止架構師使用概括描述, 強制要求它在規格書中把 "統一提交的所有賦值代碼行" 以具體語法逐行列出, 修正如下 :
我是 Python 演算法開發者。
【用戶原始需求】:【在此填入任何口語需求,例如:ATR 移動停損 / 海龜突破 / 雙均線交叉 / 動態網格交易】
【架構師前置任務(自動補齊規格)】:
在生成 Prompt 前,請先依量化狀態機工程規範,主動將上述需求擴充為:
1. 具體函式簽名:包含 df: pd.DataFrame、外部觸發訊號欄位(如 entry_col: str = "entry_signal")及合理預設參數。
2. 精準功能描述:明確定義「外部觸發進場條件」、「持倉期間狀態更新」與「離場機制」,嚴禁遺漏外部觸發訊號。
【硬性範疇限制】:每次提問僅限單一策略。嚴禁合併多個不同策略,嚴禁設計通用分發引擎(禁止 mode 參數)。
請注意:現在先不要寫最終的 Python 實作代碼。
請扮演資深系統架構師,梳理該策略的邏輯,並輸出一份『專門餵給本機 14B 代碼模型(Qwen 2.5 Coder 14B)執行的極簡結構化 Prompt』。
為了防止本機小模型產生「陣列長度不匹配廣播崩潰」、「偽向量化偷換數學算式」、「時序穿透」、「分支漏賦值導致狀態中斷」或「注意力倒置漏給實作」,產出的 Prompt 必須嚴格遵守以下 5 項架構設計原則:
1. 運算範式與數值計算契約(Numerical & Alignment Guard):
- 【純無副作用】:函式開頭必須強制執行 `df = df.copy()`,嚴禁修改傳入的原始物件;所有計算出的核心特徵與狀態欄位必須完整回填輸出。
- 【時序差分對齊】:凡涉及前一期資訊(t-1 lag)的向量化運算,必須明確給出切片對齊語法(當期切片 [1:] 與前期切片 [:-1] 長度對齊,第 0 筆獨立初始化),嚴禁不同長度陣列直接相減引發廣播崩潰。
- 【拒絕偽向量化】:明確區分有限滑動視窗與遞迴平滑(如 EMA、Wilder 等依賴前一期數值之濾波)。凡具備遞迴相依特徵,指示直接以純數值迴圈計算,嚴禁用 np.convolve 或滑動平均冒充。
- 【0-based 視窗邊界對齊】:凡涉及長度為 W 之視窗統計或初始暖機計算,首個有效聚合值必須精確對齊至視窗末端索引 W - 1,後續時序推進嚴格自索引 W 起算,嚴禁因 0-based 索引混淆導致邊界偏移(Off-by-one)。
- 【異常值傳播契約】:若時序中間出現缺失值(NaN),當期指標計算設為 NaN 即可;當後續數值恢復有效時,直接沿用最近一期有效狀態值延續遞迴平滑,嚴禁指示小模型設計複雜的「中斷後重新累積暖機」邏輯。
2. 迴圈拓撲與單一 Pass 限制(Single-Pass FSM Topology):
- 明確區分「可全域預先向量化特徵」與「路徑依賴狀態機」。
- 【單一迴圈紅線】:若存在狀態轉移依賴,所有相互關聯的事件、進出場判定與動態軌跡更新,必須嚴格封裝在唯一一個 `for t in range(n)` 迴圈內,嚴禁拆成多個獨立迴圈預算事件。
3. 依賴閉環與變數綁定(Dependency Closure & Memory Scaffold):
- 【依賴閉環原則】:輸入 Schema 中定義的所有決策驅動欄位,在迴圈前必須轉為 1D NumPy 陣列;且必須在狀態機迴圈的 `if` 條件分支中被顯式引用,嚴禁輸入欄位被閒置或遺忘。
- 所有輸出欄位,在進入迴圈前於記憶體預先宣告配置好 1D NumPy 陣列(禁止在迴圈內對 DataFrame 逐列賦值)。
4. 純量狀態機與統一提交架構(Scalar Register & Unified Commit):
- 【純量變數隔離】:迴圈外部初始化純量狀態暫存器(如 curr_pos=0, curr_peak=np.nan, curr_stop=np.nan)。迴圈內部各條件分支「僅允許修改純量變數」,嚴禁在 if-elif 分支內部直接對 position[t] 或 stop[t] 賦值。
- 【單向因果流動】:迴圈首行固定執行 prev_pos, prev_peak, prev_stop = curr_pos, curr_peak, curr_stop 凍結前值以防時序穿透。
- 【統一提交出口(顯式代碼清單)】:產出的 Prompt 嚴禁使用「提交所有狀態」等概括性文字。架構師必須在骨架尾端,根據該策略宣告的所有狀態純量,具體逐行寫出無條件提交的賦值代碼(格式為 arr_A[t] = curr_A、arr_B[t] = curr_B),禁止在分支內有條件落盤,杜絕狀態漏賦值。
5. 極限邊界防禦與 Negative Constraints:
- 空資料、長度不足時安全回傳帶有完整目標 Schema 的 DataFrame,嚴禁索引越界。
- 數據中斷(NaN/None)時明確規定狀態凍結或沿用前值,嚴禁使用全域 fillna(0) 或重新累積暖機破壞時序。
- 嚴格禁止使用 iterrows、itertuples、未定義欄位預先存取及拿回傳表比對原始物件。
- 產出的 Prompt 篇幅控制在 600~800 字內,要求小模型依序輸出「完整函式」與「狀態邏輯 pytest 驗證(無須手算小數)」。
Gemini 對此高約束提示詞的評論是 : 工程水準已經收斂到無懈可擊的量產級境界, 所有在實測中遇到的隱性 Bug 與注意力遺忘問題在這份規格書中全數被精準封死. 通用模板中第 4 條加入的 "統一提交出口(顯式代碼清單)" 敘述發揮了決定性作用, 產出了清晰無歧義自帶代碼錨點的極致規格.
def dynamic_grid(df: pd.DataFrame, entry_col: str = "entry_signal", grid_pct: float = 0.02,
lot_size: float = 1.0, max_positions: int = 5) -> pd.DataFrame:
required_cols = ['Close', entry_col]
if not all(col in df.columns for col in required_cols):
raise ValueError(f"Missing required columns: {', '.join(required_cols)}")
if not pd.api.types.is_numeric_dtype(df['Close']):
raise ValueError("Close must be a numeric type.")
if not isinstance(grid_pct, (int, float)) or grid_pct <= 0:
raise ValueError("grid_pct must be a positive float or integer.")
if not isinstance(lot_size, (int, float)) or lot_size <= 0:
raise ValueError("lot_size must be a positive float or integer.")
if not isinstance(max_positions, int) or max_positions <= 0 or isinstance(max_positions,
raise ValueError("max_positions must be a positive integer.")
close = df['Close'].values
entry_signal = df[entry_col].values.astype(bool)
grid_level_arr = np.full(n, np.nan)
next_buy_arr = np.full(n, np.nan)
next_sell_arr = np.full(n, np.nan)
position_arr = np.zeros(n, dtype=int)
entry_event_arr = np.zeros(n, dtype=bool)
buy_event_arr = np.zeros(n, dtype=bool)
sell_event_arr = np.zeros(n, dtype=bool)
exit_event_arr = np.zeros(n, dtype=bool)
valid_close = np.isfinite(close[t])
valid_entry = entry_signal[t]
if prev_pos == 0 and valid_entry and valid_close:
curr_buy = curr_grid * (1 - grid_pct)
curr_sell = curr_grid * (1 + grid_pct)
if curr_pos < max_positions and close[t] <= curr_buy:
curr_buy = prev_buy * (1 - grid_pct)
curr_sell = prev_sell * (1 - grid_pct)
elif curr_pos > 0 and close[t] >= curr_sell:
curr_buy = prev_buy * (1 + grid_pct)
curr_sell = prev_sell * (1 + grid_pct)
if curr_pos == 0 and prev_pos > 0:
grid_level_arr[t] = curr_grid
next_buy_arr[t] = curr_buy
next_sell_arr[t] = curr_sell
position_arr[t] = curr_pos
entry_event_arr[t] = curr_entry_event
buy_event_arr[t] = curr_buy_event
sell_event_arr[t] = curr_sell_event
exit_event_arr[t] = curr_exit_event
df['Grid_Level'] = grid_level_arr
df['Next_Buy'] = next_buy_arr
df['Next_Sell'] = next_sell_arr
df['Position'] = position_arr
df['Entry_Event'] = entry_event_arr
df['Buy_Event'] = buy_event_arr
df['Sell_Event'] = sell_event_arr
df['Exit_Event'] = exit_event_arr
'Close': [100, 102, 103, 98, 95, 97, 100, 101, 102, 103, 104],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df)
assert 'Grid_Level' in result.columns
assert 'Next_Buy' in result.columns
assert 'Next_Sell' in result.columns
assert 'Position' in result.columns
assert 'Entry_Event' in result.columns
assert 'Buy_Event' in result.columns
assert 'Sell_Event' in result.columns
assert 'Exit_Event' in result.columns
df = pd.DataFrame(columns=['Close', 'entry_signal'])
result = dynamic_grid(df)
assert 'Grid_Level' in result.columns
assert 'Next_Buy' in result.columns
assert 'Next_Sell' in result.columns
assert 'Position' in result.columns
assert 'Entry_Event' in result.columns
assert 'Buy_Event' in result.columns
assert 'Sell_Event' in result.columns
assert 'Exit_Event' in result.columns
def test_parameter_validation():
'Close': [100, 102, 103, 98, 95, 97, 100, 101, 102, 103, 104],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
dynamic_grid(df, grid_pct=-0.02)
assert str(e) == "grid_pct must be a positive float or integer."
dynamic_grid(df, lot_size=-1.0)
assert str(e) == "lot_size must be a positive float or integer."
dynamic_grid(df, max_positions=-5)
assert str(e) == "max_positions must be a positive integer."
'Close': [100, np.nan, 103, 98, np.nan, 97, 100, 101, 102, 103, 104],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df)
assert 'Grid_Level' in result.columns
assert 'Next_Buy' in result.columns
assert 'Next_Sell' in result.columns
assert 'Position' in result.columns
assert 'Entry_Event' in result.columns
assert 'Buy_Event' in result.columns
assert 'Sell_Event' in result.columns
assert 'Exit_Event' in result.columns
def test_no_entry_no_position():
'Close': [100, 102, 103, 98, 95, 97, 100, 101, 102, 103, 104],
'entry_signal': [False, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df)
assert all(result['Position'] == 0)
assert all(result['Grid_Level'].isna())
assert all(result['Next_Buy'].isna())
assert all(result['Next_Sell'].isna())
assert all(result['Entry_Event'] == False)
assert all(result['Buy_Event'] == False)
assert all(result['Sell_Event'] == False)
assert all(result['Exit_Event'] == False)
def test_entry_and_trading():
'Close': [100, 102, 103, 98, 95, 97, 100, 101, 102, 103, 104],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df)
assert result['Entry_Event'][0] == True
assert result['Position'][0] == 1
assert result['Grid_Level'][0] == 100
assert result['Next_Buy'][0] == 98
assert result['Next_Sell'][0] == 102
assert all(result['Buy_Event'][1:5] == False)
assert result['Buy_Event'][5] == True
assert result['Position'][5] == 2
assert result['Next_Buy'][5] == 96
assert result['Next_Sell'][5] == 100
assert all(result['Sell_Event'][6:] == False)
assert result['Sell_Event'][9] == True
assert result['Position'][9] == 1
assert result['Next_Buy'][9] == 98
assert result['Next_Sell'][9] == 102
assert result['Exit_Event'][10] == True
assert result['Position'][10] == 0
def test_max_positions():
'Close': [100, 102, 103, 98, 95, 97, 100, 101, 102, 103, 104],
'entry_signal': [True, False, False, False, False, False, False, False, False, False,
result = dynamic_grid(df, max_positions=2)
assert result['Entry_Event'][0] == True
assert result['Position'][0] == 1
assert result['Grid_Level'][0] == 100
assert result['Next_Buy'][0] == 98
assert result['Next_Sell'][0] == 102
assert all(result['Buy_Event'][1:5] == False)
assert result['Buy_Event'][5] == False
assert result['Position'][5] == 1
assert result['Next_Buy'][5] == 98
assert result['Next_Sell'][5] == 102
assert all(result['Sell_Event'][6:] == False)
assert result['Exit_Event'][10] == True
assert result['Position'][10] == 0
'Close': [100, 102, 103, 98, 95, 97, 100, 101, 102, 103, 104],
'entry_signal': [True, False, False, False, False, True, False, False, False, False,
result = dynamic_grid(df)
assert result['Entry_Event'][0] == True
assert result['Position'][0] == 1
assert result['Grid_Level'][0] == 100
assert result['Next_Buy'][0] == 98
assert result['Next_Sell'][0] == 102
assert all(result['Buy_Event'][1:5] == False)
assert result['Buy_Event'][5] == False
assert result['Position'][5] == 1
assert result['Next_Buy'][5] == 98
assert result['Next_Sell'][5] == 102
assert all(result['Sell_Event'][6:] == False)
assert result['Exit_Event'][10] == True
assert result['Position'][10] == 0
Gemini 對此新程式碼的審查結果認為, 這次 Qwen 生成的代碼證實 "顯式代碼清單" 帶來的威力非常明顯, 上次把 position 丟掉的致命 Bug 被直接徹底修正, 8 個輸出陣列全部在迴圈末端精確落盤, 並且附帶了完整的輸入驗證與大量 pytest 單元測試. 成果可以歸納為如下三點 :
不過, 此程式碼在業務邏輯方面存在一些缺陷, 例如清倉時未重置網格水位 (curr_pos == 0 時未清除狀態) (第 65~73 行), Prompt 規格明確要求 "持倉歸零即結束週期並清除網格; 清倉當根不得重新進場", 但 Qwen 顯然沒有遵行; 其次是加減碼的網格推進公式有複利累積偏差: 最後是測試中存在手算假資料, 這是 Qwen 的老毛病, 雖然規格有寫 "不必手算小數", 但它在寫測試時還是手癢寫了一堆 hardcode 數值, 跑 pytest 時浮點數的四捨五入就會噴錯.