下單(單策略)
執行交易策略
目前交易系統僅支援 finlab>=0.3.0.dev1 版本。在下單前,請先確認安裝了新版的 Package 喔!
首先,在下一個交易日開盤前,執行策略:
計算股票張數
接下來,顯示最近的部位
| stock_id | entry_date | exit_date | entry_sig_date | exit_sig_date | position | period | entry_index | exit_index | return | entry_price | exit_price | mae | gmfe | bmfe | mdd | pdays | next_weights |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1416 廣豐 | 2021-10-01 | NaT | 2021-09-30 | 2022-06-30 | 0.510197 | 159.0 | 3566.0 | -1.0 | -3.539823e-02 | 10.60 | NaN | -0.053097 | 0.070796 | 0.070796 | -0.115702 | 37.0 | 0.060665 |
| 1453 大將 | 2021-10-01 | NaT | 2021-09-30 | 2022-06-30 | 0.510197 | 159.0 | 3566.0 | -1.0 | 2.348165e+00 | 8.43 | NaN | -0.086763 | 2.403782 | 0.245829 | -0.624585 | 60.0 | 0.171264 |
| 1524 耿鼎 | 2022-04-01 | NaT | 2022-03-31 | 2022-06-30 | 0.611632 | 39.0 | 3686.0 | -1.0 | -3.330669e-16 | 10.60 | NaN | -0.476658 | 0.015152 | 0.000000 | -0.476658 | 2.0 | 0.122168 |
| 2543 皇昌 | 2021-10-01 | NaT | 2021-09-30 | NaT | 0.510197 | 159.0 | 3566.0 | -1.0 | 1.073232e-01 | 7.46 | NaN | -0.063131 | 0.363636 | 0.008838 | -0.268519 | 44.0 | 0.070298 |
| 2701 萬企 | 2022-04-01 | NaT | 2022-03-31 | 2022-06-30 | 0.611632 | 39.0 | 3686.0 | -1.0 | -3.330669e-16 | 12.05 | NaN | -0.025105 | 0.029289 | 0.029289 | -0.052846 | 18.0 | 0.063107 |
假如確認沒問題,可以計算每檔股票投資的張數:
from finlab.online.order_executor import Position
# total fund
fund = 1000000
position = Position.from_report(report, fund)
print(position)
上述 Position 代表您的帳戶中,只希望有一張 2330 台積電股票,並且是現貨。
進階部位調整
零股交易
欲使用零股部位,只要將上述程式碼做以下修改即可:
# 整股
position = Position.from_report(report, fund)
# 零股
position = Position.from_report(report, fund, odd_lot=True)
客製化部位張數
上述方法使用 Position.from_report 來建構部位,其實我們也可以單純用很直覺的方式建構想要再平衡的部位:
[{'stock_id': '2330', 'quantity': 1, 'order_condition': <OrderCondition.CASH: 1>}
{'stock_id': '1101', 'quantity': 1.001, 'order_condition': <OrderCondition.CASH: 1>}]
部位增減調整
Position 是可以相加減的,我們可以用以下方法調整部位大小
# 減一張 2330 股票
new_position = position - Position({'2330': 1})
# 增加一張 1101 股票
new_position = position + Position({'1101': 1})
多策略權重加總
假如我們有多個 position,可以用以下方式加總:
from finlab import backtest
from finlab.online.order_executor import Position
report1 = backtest.sim(...)
report2 = backtest.sim(...)
position1 = Position.from_report(report1, 1000000) # 策略操作金額一百萬
position2 = Position.from_report(report2, 1000000) # 策略操作金額一百萬
total_position = position1 + position2
實際下單
1. 安裝券商 API
目前支援 玉山證券、永豐證券、元富證券 及 富邦證券 的下單系統,擇一使用即可。
2. 連接證券帳戶
目前支援 玉山證券、永豐證券、元富證券 以及 富邦證券 的下單系統。可以先將帳號密碼設定成環境變數,只要針對您需要的券商來設定即可,不需要多個券商同時串接。
選擇您的券商
from finlab.online.esun_account import EsunAccount
import os
os.environ['ESUN_CONFIG_PATH'] = '玉山證券交易設定檔(config.ini.example)路徑'
os.environ['ESUN_MARKET_API_KEY'] = '玉山證券的行情API Token'
os.environ['ESUN_ACCOUNT_PASSWORD'] = '玉山證券的帳號密碼'
os.environ['ESUN_CERT_PASSWORD'] = '玉山證券的憑證密碼'
acc = EsunAccount()
舊版 Fugle 環境變數(仍可使用)
from finlab.online.fugle_account import FugleAccount
import os
os.environ['FUGLE_CONFIG_PATH'] = '玉山證券交易設定檔(config.ini.example)路徑'
os.environ['FUGLE_MARKET_API_KEY'] = '玉山證券的行情API Token'
os.environ['FUGLE_ACCOUNT_PASSWORD'] = '玉山證券的帳號密碼'
os.environ['FUGLE_CERT_PASSWORD'] = '玉山證券的憑證密碼'
acc = FugleAccount()
假如使用中下單出現錯誤代碼,可以到玉山證券文件 來查找原因。假如對於 Finlab Package 下單不熟習,建議也可以先按照券商的教學來練習,假如串接成功,則 FinLab Package 的串接也就是如法炮製的設定囉!
-
請先獲取憑證
- Windows 憑證下載方式
- MacOS 憑證下載:目前永豐雖支援 MacOS 下單,但並不支援用 MacOS 獲取憑證,可以先找一台 Windows 作業系統,並且使用上述方法獲取憑證後,再將憑證於 MacOS 來使用。
import os
from finlab.online.sinopac_account import SinopacAccount
os.environ['SHIOAJI_API_KEY'] = '永豐證券API_KEY'
os.environ['SHIOAJI_SECRET_KEY'] = '永豐證券SECRET_KEY'
os.environ['SHIOAJI_CERT_PERSON_ID']= '身份證字號'
os.environ['SHIOAJI_CERT_PATH']= '永豐證券憑證路徑'
os.environ['SHIOAJI_CERT_PASSWORD'] = '永豐證券憑證密碼' # 預設與身份證字號
acc = SinopacAccount()
請參考元富證券的教學 來獲取憑證,並且將憑證放在相對應的路徑之中,並且安裝相對應的套件後,即可執行:
import os
from finlab.online.masterlink_account import MasterlinkAccount
os.environ['MASTERLINK_NATIONAL_ID'] = '身分證字號'
os.environ['MASTERLINK_ACCOUNT'] = '交易帳號'
os.environ['MASTERLINK_ACCOUNT_PASS'] = '密碼'
os.environ['MASTERLINK_CERT_PATH'] = '元富證券憑證路徑'
os.environ['MASTERLINK_CERT_PASS'] = '元富證券憑證密碼' # 預設與身分證字號
acc = MasterlinkAccount()
即可完成設定!
請參考富邦證券的教學 來獲取憑證,並且將憑證放在相對應的路徑之中,並且安裝相對應的套件後,即可執行:
import os
from fubon_account import FubonAccount
# 設定環境變數
import os
os.environ['FUBON_NATIONAL_ID'] = "A123456789"
os.environ['FUBON_ACCOUNT_PASS'] = "your_password"
os.environ['FUBON_CERT_PATH'] = "/path/to/cert.pfx"
account = FubonAccount()
即可完成設定!
3. 批次下單
最後利用 OrderExecuter 將當證券帳戶的部位,按照 position 的部位進行調整。
from finlab.online.order_executor import OrderExecutor
# Order Executer
order_executer = OrderExecutor(position , account=acc)
若下單部位含有「 全額交割股」、「處置股」、「警示股」,需先於證券帳戶圈存。參考富果圈存、永豐圈存方式。
根據show_alerting_stocks顯示結果進行圈存後,即可繼續執行下單。
# 下單檢查(瀏覽模式,不會真的下單)
order_executer.create_orders(view_only=True)
# 執行下單(會真的下單,初次使用建議收市時測試)
# 預設使用最近一筆成交價當成限價
order_executer.create_orders()
# 更新限價(將最後一筆成交價當成新的限價)
order_executer.update_order_price()
# 刪除所有委託單
order_executer.cancel_orders()
查看帳戶部位
不論是永豐還是玉山證券帳戶,都可以用以下方式查找帳戶的部位
# 選擇欲使用的券商(擇一即可)
# 永豐
from finlab.online.sinopac_account import SinopacAccount
acc = SinopacAccount()
# 玉山證券
from finlab.online.esun_account import EsunAccount
acc = EsunAccount()
# 或使用舊版名稱(仍可使用)
# from finlab.online.fugle_account import FugleAccount
# acc = FugleAccount()
print(acc.get_position())
[{'stock_id': '2330', 'quantity': 1, 'order_condition': <OrderCondition.CASH: 1>}
{'stock_id': '1101', 'quantity': 1.001, 'order_condition': <OrderCondition.CASH: 1>}]
常見錯誤與解決方法
⚠️ 重要風險警告
實盤交易涉及真實金錢,錯誤可能導致財務損失!
下單前務必檢查:
1. ✅ 先在模擬帳戶測試至少 1 週
2. ✅ 初期資金不超過總資金的 10-20%
3. ✅ 使用 view_only=True 預覽訂單
4. ✅ 每日監控持倉與訂單狀態
5. ✅ 設定停損機制,避免單次巨大虧損
錯誤 1:帳戶連線失敗
現象:執行 acc = SinopacAccount() 或 acc = EsunAccount() 時拋出連線錯誤
from finlab.online.sinopac_account import SinopacAccount
acc = SinopacAccount()
# ConnectionError: 無法連線至券商 API
原因: - 環境變數未正確設定(API KEY、憑證路徑等) - 憑證檔案不存在或路徑錯誤 - 憑證密碼錯誤 - 網路連線問題或券商 API 服務暫停
解決方法:
import os
from finlab.online.sinopac_account import SinopacAccount
# 步驟 1:檢查環境變數是否設定
required_env_vars = [
'SHIOAJI_API_KEY',
'SHIOAJI_SECRET_KEY',
'SHIOAJI_CERT_PERSON_ID',
'SHIOAJI_CERT_PATH',
'SHIOAJI_CERT_PASSWORD'
]
print("=== 環境變數檢查 ===")
for var in required_env_vars:
value = os.environ.get(var)
if value:
# 隱藏敏感資訊
if 'KEY' in var or 'PASSWORD' in var:
print(f"✅ {var}: {'*' * 8} (已設定)")
else:
print(f"✅ {var}: {value}")
else:
print(f"❌ {var}: 未設定")
# 步驟 2:檢查憑證檔案是否存在
cert_path = os.environ.get('SHIOAJI_CERT_PATH')
if cert_path and os.path.exists(cert_path):
print(f"\n✅ 憑證檔案存在:{cert_path}")
else:
print(f"\n❌ 憑證檔案不存在:{cert_path}")
print("請確認憑證路徑是否正確")
exit(1)
# 步驟 3:嘗試連線並捕捉錯誤
try:
acc = SinopacAccount()
print("\n✅ 帳戶連線成功")
# 驗證帳戶狀態
balance = acc.get_balance()
print(f"✅ 可用餘額:NT$ {balance:,.0f}")
except FileNotFoundError as e:
print(f"\n❌ 憑證檔案錯誤:{e}")
print("請檢查 SHIOAJI_CERT_PATH 是否正確")
except PermissionError as e:
print(f"\n❌ 憑證權限錯誤:{e}")
print("請確認憑證密碼是否正確(SHIOAJI_CERT_PASSWORD)")
except ConnectionError as e:
print(f"\n❌ 網路連線錯誤:{e}")
print("可能原因:")
print("1. 網路連線不穩定")
print("2. 券商 API 服務暫時無法使用")
print("3. API KEY 或 SECRET KEY 錯誤")
print("請稍後再試或聯繫券商客服")
except Exception as e:
print(f"\n❌ 未知錯誤:{e}")
print("請檢查所有環境變數是否正確設定")
print("參考教學:https://doc.finlab.tw/details/order_api/")
玉山證券專用檢查:
import os
from finlab.online.esun_account import EsunAccount
# 檢查玉山證券環境變數
required_vars = [
'ESUN_CONFIG_PATH',
'ESUN_MARKET_API_KEY',
'ESUN_ACCOUNT_PASSWORD',
'ESUN_CERT_PASSWORD'
]
for var in required_vars:
if not os.environ.get(var):
print(f"❌ {var} 未設定")
print(f" 請參考:https://www.esunsec.com.tw/trading-platforms/api-trading/docs/trading/quick-start/")
exit(1)
# 檢查 config.ini 是否存在
config_path = os.environ.get('ESUN_CONFIG_PATH')
if not os.path.exists(config_path):
print(f"❌ Config 檔案不存在:{config_path}")
exit(1)
try:
acc = EsunAccount()
print("✅ 玉山證券帳戶連線成功")
except Exception as e:
print(f"❌ 連線失敗:{e}")
print("請檢查 config.ini 內容是否正確")
錯誤 2:下單前安全檢查失敗
現象:資金不足、持倉過多或單股權重過高
原因:
- 可用資金不足以買入 position 指定的股票
- 策略產生的持股數量超過風險限制
- 單一股票權重過高(未分散風險)
解決方法:實施多層安全檢查
from finlab.online.order_executor import OrderExecutor, Position
from finlab.online.sinopac_account import SinopacAccount
def safe_order_check(position, account, max_stocks=50, max_weight=0.15, min_cash_buffer=0.1):
"""
下單前安全檢查函數
Args:
position: Position 物件
account: 券商帳戶物件
max_stocks: 最大持股數量(預設 50 檔)
max_weight: 單股最大權重(預設 15%)
min_cash_buffer: 最小現金緩衝比例(預設 10%)
Raises:
ValueError: 檢查不通過時拋出錯誤
"""
print("=== 下單安全檢查 ===\n")
# 檢查 1:持倉數量
num_stocks = len(position)
print(f"持倉數量:{num_stocks} 檔")
if num_stocks > max_stocks:
raise ValueError(
f"❌ 持倉過多({num_stocks} 檔),超過限制 {max_stocks} 檔\n"
f" 建議:使用 .is_largest(N) 限制選股數量"
)
print(f"✅ 持倉數量檢查通過(< {max_stocks} 檔)\n")
# 檢查 2:單一持股權重
# 假設 position 是 dict 格式 {'stock_id': quantity}
total_value = sum(position.values()) # 總價值(張數總和)
for stock_id, quantity in position.items():
weight = quantity / total_value if total_value > 0 else 0
print(f" {stock_id}: {quantity:.2f} 張 ({weight:.1%})")
if weight > max_weight:
raise ValueError(
f"❌ 單股權重過高:{stock_id} 佔 {weight:.1%},超過限制 {max_weight:.0%}\n"
f" 建議:使用 position_limit 參數限制單股權重"
)
print(f"\n✅ 單股權重檢查通過(< {max_weight:.0%})\n")
# 檢查 3:可用資金
available_cash = account.get_balance()
print(f"可用資金:NT$ {available_cash:,.0f}")
# 估算所需資金(簡化計算,實際需要考慮股價)
# 這裡假設 position.get_required_cash() 方法存在
# required_cash = position.get_required_cash()
# 簡化版:假設平均每張 50,000 元
estimated_cost = total_value * 50000
print(f"預估成本:NT$ {estimated_cost:,.0f}")
cash_buffer = available_cash * min_cash_buffer
if estimated_cost > (available_cash - cash_buffer):
raise ValueError(
f"❌ 資金不足!\n"
f" 可用資金:NT$ {available_cash:,.0f}\n"
f" 預估成本:NT$ {estimated_cost:,.0f}\n"
f" 現金緩衝:NT$ {cash_buffer:,.0f}(保留 {min_cash_buffer:.0%})\n"
f" 建議:降低總資金比例或追加資金"
)
print(f"✅ 資金檢查通過(剩餘 {min_cash_buffer:.0%} 緩衝)\n")
# 檢查 4:處置股/警示股檢查(需要額外資料)
# 這部分在實際使用時需要調用 show_alerting_stocks()
print("⚠️ 提醒:下單前請執行 order_executer.show_alerting_stocks() 檢查處置股\n")
print("=" * 50)
print("✅ 所有安全檢查通過,可以執行下單")
print("=" * 50)
# 使用範例
try:
acc = SinopacAccount()
position = Position.from_report(report, fund=1000000)
# 執行安全檢查
safe_order_check(position, acc, max_stocks=30, max_weight=0.10)
# 通過檢查後,執行下單
order_executor = OrderExecutor(position, account=acc)
# 先預覽(不會真的下單)
print("\n=== 預覽訂單 ===")
order_executor.create_orders(view_only=True)
# 確認無誤後,執行下單
# order_executor.create_orders() # 取消註解以實際下單
except ValueError as e:
print(f"\n{e}")
print("\n請修正問題後再執行下單")
except Exception as e:
print(f"\n❌ 未預期的錯誤:{e}")
print("請聯繫技術支援或查看詳細錯誤訊息")
錯誤 3:訂單被拒絕
現象:執行 create_orders() 後,部分或全部訂單被券商拒絕
常見拒單原因:
- 處置股未圈存
- 股票暫停交易
- 漲跌停鎖死
- 零股交易時段錯誤
- 帳戶權限不足
解決方法:
from finlab.online.order_executor import OrderExecutor
# 建立 OrderExecutor
order_executor = OrderExecutor(position, account=acc)
# 步驟 1:檢查處置股/警示股
print("=== 檢查處置股 ===")
alerting_stocks = order_executor.show_alerting_stocks()
if alerting_stocks:
print("⚠️ 發現處置股/警示股,請至券商平台圈存後再下單")
print("參考圈存教學:")
print(" - 富果:https://support.fugle.tw/trading/trading-troubleshoot/5231/")
print(" - 永豐:https://www.sinotrade.com.tw/richclub/freshman/...")
# 等待用戶手動圈存後再繼續
input("圈存完成後,按 Enter 繼續...")
# 步驟 2:檢查交易時段
from datetime import datetime
now = datetime.now()
hour = now.hour
minute = now.minute
if 9 <= hour < 13 or (hour == 13 and minute <= 30):
print("✅ 當前為交易時段(09:00-13:30)")
else:
print("⚠️ 當前為非交易時段")
print(" 整股交易時段:09:00-13:30")
print(" 零股交易時段:09:00-13:40(盤中)、14:00-14:30(盤後)")
# 步驟 3:先預覽,再下單
print("\n=== 預覽訂單 ===")
order_executor.create_orders(view_only=True)
confirmation = input("\n確認無誤後輸入 'YES' 執行下單:")
if confirmation == 'YES':
try:
# 執行下單
orders = order_executor.create_orders()
# 檢查訂單狀態
print("\n=== 訂單狀態 ===")
for order in orders:
print(f"{order['stock_id']}: {order['status']}")
# 統計成功/失敗
success_count = sum(1 for o in orders if o['status'] == 'success')
fail_count = len(orders) - success_count
if fail_count > 0:
print(f"\n⚠️ {fail_count} 筆訂單失敗,請檢查拒單原因:")
for order in orders:
if order['status'] != 'success':
print(f" {order['stock_id']}: {order['error_message']}")
print("\n常見拒單原因:")
print("1. 處置股未圈存")
print("2. 股票暫停交易(停牌、全額交割)")
print("3. 漲跌停鎖死無法成交")
print("4. 零股交易時段錯誤")
print("5. 資金不足")
else:
print(f"\n✅ 所有訂單成功送出({success_count} 筆)")
except Exception as e:
print(f"\n❌ 下單失敗:{e}")
print("請檢查網路連線或聯繫券商客服")
else:
print("已取消下單")
錯誤 4:零股下單失敗
現象:使用 odd_lot=True 但訂單失敗
position = Position.from_report(report, fund, odd_lot=True)
order_executor = OrderExecutor(position, account=acc)
order_executor.create_orders()
# 錯誤:零股交易時段不符
原因: - 零股交易時段限制(盤中 09:00-13:40、盤後 14:00-14:30) - 部分券商不支援零股 API 交易 - 零股成交量不足
解決方法:
from datetime import datetime
from finlab.online.order_executor import Position, OrderExecutor
# 檢查是否在零股交易時段
now = datetime.now()
hour, minute = now.hour, now.minute
# 盤中零股:09:00-13:40
is_intraday_odd_lot = (9 <= hour < 13) or (hour == 13 and minute <= 40)
# 盤後零股:14:00-14:30
is_afterhours_odd_lot = (hour == 14 and 0 <= minute <= 30)
if not (is_intraday_odd_lot or is_afterhours_odd_lot):
print("⚠️ 當前不在零股交易時段")
print(" 盤中零股:09:00-13:40")
print(" 盤後零股:14:00-14:30")
print("\n建議:")
print("1. 等待零股交易時段再下單")
print("2. 改用整股交易(移除 odd_lot=True)")
exit(0)
# 建立零股部位
position = Position.from_report(report, fund=500000, odd_lot=True)
print(f"✅ 當前在零股交易時段,可執行下單")
# 執行下單
try:
order_executor = OrderExecutor(position, account=acc)
order_executor.create_orders(view_only=True) # 先預覽
# order_executor.create_orders() # 確認後取消註解
except Exception as e:
print(f"❌ 零股下單失敗:{e}")
print("\n可能原因:")
print("1. 券商不支援零股 API 交易(請聯繫券商確認)")
print("2. 零股成交量不足(建議改用整股)")
print("3. 網路連線問題")
實盤交易最佳實踐
1. 使用模擬帳戶測試
2. 設定每日下單排程
import schedule
import time
def daily_rebalance():
"""每日收盤後自動調倉"""
try:
# 1. 執行策略
report = backtest.sim(position, resample='M')
# 2. 計算部位
position = Position.from_report(report, fund=1000000)
# 3. 安全檢查
safe_order_check(position, acc)
# 4. 執行下單
order_executor = OrderExecutor(position, account=acc)
order_executor.create_orders()
print(f"✅ {datetime.now()} - 下單完成")
except Exception as e:
print(f"❌ {datetime.now()} - 下單失敗:{e}")
# 發送通知(LINE/Email)
# 每日 14:00 執行(收盤後)
schedule.every().day.at("14:00").do(daily_rebalance)
while True:
schedule.run_pending()
time.sleep(60)
3. 記錄所有交易
import json
from datetime import datetime
def log_trade(position, orders, status):
"""記錄交易日誌"""
log_entry = {
'timestamp': datetime.now().isoformat(),
'position': position,
'orders': orders,
'status': status
}
with open('trade_log.json', 'a') as f:
f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
參考資源
- API Reference - online - OrderExecutor 完整參數說明
- 實盤下單教學(多策略) - 多策略組合下單
- 風險管理完整指南 - 實盤風險控制
- 完整策略開發流程 - 從回測到實盤
- 常見問題 FAQ - 更多疑難排解
最後提醒
請務必在模擬帳戶充分測試後,才使用真實資金!
建議測試清單: - ✅ 帳戶連線穩定無錯誤 - ✅ 下單流程完整執行 - ✅ 持倉計算正確(與預期一致) - ✅ 資金控制正常(不超支) - ✅ 異常處理機制有效 - ✅ 連續運行 1-2 週無問題
只有在以上項目全部確認後,才考慮使用真實資金!