Skip to content

Order (Single Strategy)

Broker Integration - Taiwan Market

The live order execution system currently integrates with Taiwan-based brokers (E.SUN Securities, Sinopac Securities, Masterlink Securities, and Fubon Securities). For US market trading, please use your broker's native API. The position calculation and portfolio management features of FinLab can still be used to generate target positions for any market.

Execute Trading Strategy

The current trading system requires finlab>=0.3.0.dev1. Before placing orders, please make sure you have the latest version installed. First, run your strategy before the next trading day opens:

from finlab import backtest

# write your own strategy

report = backtest.sim(...)

Calculate Share Quantities

Next, display the current positions:

print(report.current_trades)
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

Once confirmed, calculate the number of lots (1 lot = 1000 shares in Taiwan) for each stock:

from finlab.online.order_executor import Position

# total fund
fund = 1000000
position = Position.from_report(report, fund)
print(position)
This produces the following result:
[{'stock_id': '2330', 'quantity': 1, 'order_condition': <OrderCondition.CASH: 1>}]

The Position above indicates that your account should hold 1 lot of 2330 (TSMC), using cash settlement.

Advanced Position Adjustments

Odd Lot Trading

To use odd lot positions, simply modify the code as follows:

# Round lots
position = Position.from_report(report, fund)

# Odd lots
position = Position.from_report(report, fund, odd_lot=True)

Custom Position Quantities

The above method uses Position.from_report to construct positions, but you can also build positions intuitively:

position = Position({'2330': 1, '1101': 1.001})
This produces the following result:
[{'stock_id': '2330', 'quantity': 1, 'order_condition': <OrderCondition.CASH: 1>}
 {'stock_id': '1101', 'quantity': 1.001, 'order_condition': <OrderCondition.CASH: 1>}]

Position Addition/Subtraction

Position objects support addition and subtraction:

# Remove 1 lot of 2330
new_position = position - Position({'2330': 1})
# Add 1 lot of 1101
new_position = position + Position({'1101': 1})

Multi-Strategy Position Aggregation

If you have multiple positions, you can aggregate them:

from finlab import backtest
from finlab.online.order_executor import Position

report1 = backtest.sim(...)
report2 = backtest.sim(...)

position1 = Position.from_report(report1, 1000000) # Strategy fund: 1 million
position2 = Position.from_report(report2, 1000000) # Strategy fund: 1 million

total_position = position1 + position2

Placing Orders

1. Install Broker API

Currently supports E.SUN Securities, Sinopac Securities, Masterlink Securities, and Fubon Securities trading systems. Choose one:

# E.SUN Securities
pip install esun-trade

# Sinopac
pip install shioaji

2. Connect to Brokerage Account

Currently supports E.SUN Securities, Sinopac Securities, Masterlink Securities, and Fubon Securities trading systems. You can set the account credentials as environment variables. You only need to configure for the broker you use; no need to connect multiple brokers simultaneously.

Choose your broker

from finlab.online.esun_account import EsunAccount
import os
os.environ['ESUN_CONFIG_PATH'] = 'path to config.ini.example'
os.environ['ESUN_MARKET_API_KEY'] = 'E.SUN market API Token'
os.environ['ESUN_ACCOUNT_PASSWORD'] = 'E.SUN account password'
os.environ['ESUN_CERT_PASSWORD'] = 'E.SUN certificate password'

acc = EsunAccount()
Legacy Fugle environment variables (still supported)
from finlab.online.fugle_account import FugleAccount
import os
os.environ['FUGLE_CONFIG_PATH'] = 'path to config.ini.example'
os.environ['FUGLE_MARKET_API_KEY'] = 'market API Token'
os.environ['FUGLE_ACCOUNT_PASSWORD'] = 'account password'
os.environ['FUGLE_CERT_PASSWORD'] = 'certificate password'

acc = FugleAccount()

If you encounter error codes during trading, check the E.SUN Securities documentation for the cause. If you are unfamiliar with FinLab Package trading, it is also recommended to practice with the broker's tutorial first.

  • Please obtain the certificate first:

    • Windows certificate download
    • MacOS certificate: Currently Sinopac supports MacOS for trading but does not support obtaining certificates on MacOS. You can first use a Windows machine to obtain the certificate, then use it on MacOS.

import os
from finlab.online.sinopac_account import SinopacAccount

os.environ['SHIOAJI_API_KEY'] = 'Sinopac API_KEY'
os.environ['SHIOAJI_SECRET_KEY'] = 'Sinopac SECRET_KEY'
os.environ['SHIOAJI_CERT_PERSON_ID']= 'National ID number'
os.environ['SHIOAJI_CERT_PATH']= 'Sinopac certificate path'
os.environ['SHIOAJI_CERT_PASSWORD'] = 'Sinopac certificate password' # defaults to National ID

acc = SinopacAccount()
If you are unfamiliar with FinLab Package trading, it is recommended to practice with the broker's tutorial first.

Refer to the Masterlink Securities tutorial to obtain the certificate, place it in the appropriate path, install the required packages, and then run:

import os
from finlab.online.masterlink_account import MasterlinkAccount

os.environ['MASTERLINK_NATIONAL_ID'] = 'National ID number'
os.environ['MASTERLINK_ACCOUNT'] = 'Trading account'
os.environ['MASTERLINK_ACCOUNT_PASS'] = 'Password'
os.environ['MASTERLINK_CERT_PATH'] = 'Masterlink certificate path'
os.environ['MASTERLINK_CERT_PASS'] = 'Masterlink certificate password' # defaults to National ID

acc = MasterlinkAccount()

Setup is now complete!

Refer to the Fubon Securities tutorial to obtain the certificate, place it in the appropriate path, install the required packages, and then run:

import os
from fubon_account import FubonAccount

# Set environment variables
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()

Setup is now complete!

3. Batch Order Execution

Finally, use OrderExecutor to adjust the brokerage account's positions according to the target position.

from finlab.online.order_executor import OrderExecutor

# Order Executor
order_executer = OrderExecutor(position , account=acc)

If the position contains "full-delivery stocks", "disposition stocks", or "alert stocks", you must pre-deposit funds at your brokerage account first.

# Show if any alerting stocks exist in the position
order_executer.show_alerting_stocks()

Buy 8101 0.429 lots - estimated total: 2672.67
After completing the deposit based on show_alerting_stocks results, proceed with order execution:

# Preview orders (view-only mode, no actual orders placed)
order_executer.create_orders(view_only=True)

# Execute orders (will actually place orders; test during market close for first-time use)
# Default: uses the latest transaction price as the limit price
order_executer.create_orders()

# Update limit price (use the latest transaction price as new limit price)
order_executer.update_order_price()

# Cancel all pending orders
order_executer.cancel_orders()

View Account Positions

For any supported broker account, you can check positions as follows:

# Choose your broker (pick one)

# Sinopac
from finlab.online.sinopac_account import SinopacAccount
acc = SinopacAccount()

# E.SUN Securities
from finlab.online.esun_account import EsunAccount
acc = EsunAccount()

# Or use the legacy name (still supported)
# from finlab.online.fugle_account import FugleAccount
# acc = FugleAccount()

print(acc.get_position())
This prints the following result:
[{'stock_id': '2330', 'quantity': 1, 'order_condition': <OrderCondition.CASH: 1>}
 {'stock_id': '1101', 'quantity': 1.001, 'order_condition': <OrderCondition.CASH: 1>}]

Common Errors and Solutions

Important Risk Warning

Live trading involves real money. Errors can lead to financial losses!

Check before placing orders: 1. Test on a simulated account for at least 1 week 2. Initial capital should not exceed 10-20% of total funds 3. Use view_only=True to preview orders 4. Monitor positions and order status daily 5. Set stop-loss mechanisms to avoid large single-trade losses

Error 1: Account Connection Failure

Symptom: acc = SinopacAccount() or acc = EsunAccount() throws a connection error

from finlab.online.sinopac_account import SinopacAccount
acc = SinopacAccount()
# ConnectionError: Unable to connect to broker API

Causes: - Environment variables not correctly set (API KEY, certificate path, etc.) - Certificate file does not exist or path is wrong - Certificate password is incorrect - Network connection issue or broker API service is down

Solution:

import os
from finlab.online.sinopac_account import SinopacAccount

# Step 1: Check if environment variables are set
required_env_vars = [
    'SHIOAJI_API_KEY',
    'SHIOAJI_SECRET_KEY',
    'SHIOAJI_CERT_PERSON_ID',
    'SHIOAJI_CERT_PATH',
    'SHIOAJI_CERT_PASSWORD'
]

print("=== Environment Variable Check ===")
for var in required_env_vars:
    value = os.environ.get(var)
    if value:
        # Hide sensitive information
        if 'KEY' in var or 'PASSWORD' in var:
            print(f"{var}: {'*' * 8} (set)")
        else:
            print(f"{var}: {value}")
    else:
        print(f"{var}: NOT SET")

# Step 2: Check if certificate file exists
cert_path = os.environ.get('SHIOAJI_CERT_PATH')
if cert_path and os.path.exists(cert_path):
    print(f"\nCertificate file exists: {cert_path}")
else:
    print(f"\nCertificate file not found: {cert_path}")
    print("Please verify the certificate path is correct")
    exit(1)

# Step 3: Attempt connection and catch errors
try:
    acc = SinopacAccount()
    print("\nAccount connected successfully")
except FileNotFoundError as e:
    print(f"\nCertificate file error: {e}")
    print("Check SHIOAJI_CERT_PATH")
except PermissionError as e:
    print(f"\nCertificate permission error: {e}")
    print("Check certificate password (SHIOAJI_CERT_PASSWORD)")
except ConnectionError as e:
    print(f"\nNetwork connection error: {e}")
    print("Possible causes:")
    print("1. Unstable network connection")
    print("2. Broker API service temporarily unavailable")
    print("3. Incorrect API KEY or SECRET KEY")
except Exception as e:
    print(f"\nUnknown error: {e}")
    print("Check all environment variables are correctly set")

Error 2: Pre-Order Safety Check Failure

Symptom: Insufficient funds, too many holdings, or single stock weight too high

Solution: Implement multi-layer safety checks

from finlab.online.order_executor import OrderExecutor, Position
from finlab.online.sinopac_account import SinopacAccount

# Use example
try:
    acc = SinopacAccount()
    position = Position.from_report(report, fund=1000000)

    # Execute order
    order_executor = OrderExecutor(position, account=acc)

    # Preview first (no actual orders)
    print("\n=== Preview Orders ===")
    order_executor.create_orders(view_only=True)

    # After confirmation, execute orders
    # order_executor.create_orders()  # Uncomment to actually place orders

except ValueError as e:
    print(f"\n{e}")
    print("\nPlease fix the issue before placing orders")

except Exception as e:
    print(f"\nUnexpected error: {e}")

Error 3: Orders Rejected

Symptom: After executing create_orders(), some or all orders are rejected by the broker

Common rejection reasons:

  1. Disposition stocks not pre-deposited
  2. Stock suspended from trading
  3. Locked at price limit (up or down)
  4. Odd lot trading outside of allowed hours
  5. Insufficient account permissions

Error 4: Odd Lot Order Failure

Symptom: Using odd_lot=True but orders fail

Causes: - Odd lot trading hour restrictions (intraday: 09:00-13:40, after-hours: 14:00-14:30) - Some brokers do not support odd lot API trading - Insufficient odd lot trading volume

Live Trading Best Practices

1. Test with a Simulated Account

# Most brokers offer simulated accounts; test for 1-2 weeks
# Confirm all workflows are working before using real funds

2. Set Up Daily Scheduling

import schedule
import time

def daily_rebalance():
    """Auto-rebalance after market close"""
    try:
        # 1. Run strategy
        report = backtest.sim(position, resample='M')

        # 2. Calculate positions
        position = Position.from_report(report, fund=1000000)

        # 3. Execute orders
        order_executor = OrderExecutor(position, account=acc)
        order_executor.create_orders()

        print(f"{datetime.now()} - Orders completed")

    except Exception as e:
        print(f"{datetime.now()} - Order failed: {e}")
        # Send notification (LINE/Email)

# Execute daily at 14:00 (after market close)
schedule.every().day.at("14:00").do(daily_rebalance)

while True:
    schedule.run_pending()
    time.sleep(60)

3. Log All Trades

import json
from datetime import datetime

def log_trade(position, orders, status):
    """Record trade log"""
    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')

Reference Resources


Final Reminder

Always test thoroughly on a simulated account before using real funds!

Recommended testing checklist: - Account connection is stable and error-free - Order workflow executes completely - Position calculations are correct (match expectations) - Capital controls work normally (no overspending) - Exception handling mechanisms are effective - Runs continuously for 1-2 weeks without issues

Only consider using real funds after all items above are confirmed!