//======================================================================
// 腳本類型: 自動交易腳本
// 頻率: 1分鐘K線, 逐筆洗價
// 交易商品: 台股
// 策略概念:
// 當沖做多策略。在開盤前半小時(09:00-09:30)滿足特定的價量條件後，
// 於指定的時間區間(09:30-10:00)內，若出現連續外盤強勢訊號，則進場做多。
// 採用固定百分比停利停損，並在下午13:20前強制平倉。
//======================================================================

// ------------------------------
// 1. 參數設定 (Inputs)
// ------------------------------
inputs:
    _ConsecutiveTicks(3),       // 連續外盤成交筆數 (例如: 3筆)
    _MinTickVolume(10),         // 每筆最小外盤成交量 (例如: 10張)
    _MinTotalVolume(200),       // 09:00-09:30 累積成交量門檻 (例如: 200張)
    _PriceCeilingPct(3),        // 09:00-09:30 區間最高價漲幅限制 (%)
    _PriceFloorPct(97),         // 09:00-09:30 區間最低價限制 (開盤價的%)
    _EntryStartTime(093000),    // 進場開始時間 (HHMMSS格式, 例如: 093000)
    _EntryEndTime(100000),      // 進場結束時間 (HHMMSS格式, 例如: 100000)
    _TakeProfit(10),            // 停利百分比 (%)
    _StopLoss(5),               // 停損百分比 (%)
    _ExitTime(132000);          // 強制出場時間 (HHMMSS格式, 例如: 132000)

// ------------------------------
// 2. 變數宣告 (Vars)
// ------------------------------
vars:
    intrabarpersist _ConsecutiveAskCount(0), // 連續外盤成交計數器 (逐筆洗價時持續存在)
    _OpenPrice_0900(0),                      // 當日09:00開盤價
    _High_0930(0),                           // 09:00至09:30期間的最高價
    _Low_0930(99999),                        // 09:00至09:30期間的最低價
    _BasicConditionsMet(false);              // 基本條件是否成立的旗標

// ------------------------------
// 3. 每日重置與即時條件追蹤
// ------------------------------
// 判斷是否為新的一天，如果是，則重置所有每日相關變數
if date <> date[1] then begin
    _OpenPrice_0900 = 0;
    _High_0930 = 0;
    _Low_0930 = 99999;
    _BasicConditionsMet = false;
    _ConsecutiveAskCount = 0;
end;

// 此腳本需設定為「逐筆洗價」模式，才能即時處理每筆成交
// 基本條件1: 追蹤連續外盤成交且每筆大於特定張數
// 使用 GetField("TickType", "Tick") = 1 來判斷最新一筆為外盤成交(Ask-side trade)
if GetField("內外盤", "Tick") = 1 and GetField("Volume", "Tick") > _MinTickVolume then
    _ConsecutiveAskCount += 1  // 若為外盤成交且量足，計數器加1
else
    _ConsecutiveAskCount = 0;  // 若條件中斷，則計數器歸零

// ------------------------------
// 4. 計算開盤前半小時的基本條件
// ------------------------------
// 於09:00後，取得當日開盤價 (此條件確保只執行一次)
if _OpenPrice_0900 = 0 and time >= 090000 then
    _OpenPrice_0900 = GetField("Open", "D")[0]; // 使用 GetField 直接取得日開盤價，最為準確

// 追蹤 09:00 到 09:30 之間的最高價與最低價
// 註：此腳本需從開盤時執行，才能正確捕捉此區間的高低點
if time > 090000 and time <= 093000 and _OpenPrice_0900 > 0 then begin
    if high > _High_0930 then _High_0930 = high; // 更新區間最高價
    if low < _Low_0930 then _Low_0930 = low;     // 更新區間最低價
end;

// 在 09:30 之後，彙總所有基本條件，並將結果存入旗標
// 使用 _BasicConditionsMet = false 的條件，可以確保此區塊只會被執行一次，提高腳本效率
if _BasicConditionsMet = false and time > 093000 then begin
    // 基本條件2: 09:00-09:30累積成交量檢查
    var:
	   _TotalVolume(0), _TotalVolumeCheck(false);
	_TotalVolume = GetField("Volume", "D")[0];
	if _TotalVolume > _MinTotalVolume then
		_TotalVolumeCheck = true;
    // 基本條件3: 09:00-09:30區間最高價檢查
    var: _PriceCeiling(0), _PriceCeilingCheck(false);
	_PriceCeiling = _High_0930; 
    if _OpenPrice_0900 > 0 and _PriceCeiling < _OpenPrice_0900 * (1 + _PriceCeilingPct / 100) then // 確保開盤價已取得，避免計算錯誤
		_PriceCeilingCheck = true;
    // 基本條件4: 09:00-09:30區間最低價檢查
    var: _PriceFloor(0), _PriceFloorCheck(false);
	_Pricefloor = _Low_0930; 
    if _OpenPrice_0900 > 0 and _PriceFloorCheck = _Low_0930 > _OpenPrice_0900 * (_PriceFloorPct / 100) then // 確保開盤價已取得，避免計算錯誤
        _PriceFloorCheck = true;

    // 彙總所有基本條件，更新旗標
    if _TotalVolumeCheck and _PriceCeilingCheck and _PriceFloorCheck then
        _BasicConditionsMet = true;
end;

// ------------------------------
// 5. 進場邏輯 (09:30 ~ 10:00)
// ------------------------------
// 檢查是否在允許的進場時間區間內
var: _IsEntryTime(false);
_IsEntryTime = (time >= _EntryStartTime and time <= _EntryEndTime);

// 檢查連續外盤成交條件是否滿足
var: _ConsecutiveOK(false);
_ConsecutiveOK = (_ConsecutiveAskCount >= _ConsecutiveTicks);

// 整合所有進場條件
// 1. 所有基本條件(_BasicConditionsMet)成立
// 2. 在進場時間區間內(_IsEntryTime)
// 3. 即時的連續外盤訊號出現(_ConsecutiveOK)
// 4. 目前帳戶無部位(position = 0)
if _BasicConditionsMet and _IsEntryTime and _ConsecutiveOK and position = 0 then begin
    SetPosition(1, Market); // 進場做多1張
end;

// ------------------------------
// 6. 出場邏輯
// ------------------------------
// 當持有多單部位時，持續檢查所有出場條件
if position > 0 and filled > 0 then begin
    var: _entry_price(0);
    _entry_price = filled; // 使用 filled 函數取得庫存部位的實際平均成交價

    // 出場條件1: 停利
    var: _take_profit_price(0);
    _take_profit_price = _entry_price * (1 + _TakeProfit / 100);
    if close >= _take_profit_price then begin
        SetPosition(0); // 獲利平倉
    end;

    // 出場條件2: 停損
    var: _stop_loss_price(0);
    _stop_loss_price = _entry_price * (1 - _StopLoss / 100);
    if close <= _stop_loss_price then begin
        SetPosition(0); // 停損平倉
    end;

    // 出場條件3: 時間到強制出場
    if time >= _ExitTime then begin
        SetPosition(0); // 時間到平倉
    end;
end;