13. Conditional Orders (FCO)

Place and manage Flexible Conditional Order types

Objective

Place and manage the full range of FCO (Flexible Conditional Order) types: GTD, Stop, Stop Limit, Trailing Stop, Trailing Stop Limit, OCO, Bull Bear — plus querying and cancelling FCO orders.

Flow

Authenticate (OTP) → Trading API (Place FCO) → Wait to trigger (WAIT) → Underlying order placed once condition is met
  1. Authenticate with OTP via Auth / Trading.
  2. Call the place_fco_* method matching the conditional order type you need.
  3. The order is stored in a waiting state (FCOStatus.WAIT) until its trigger condition is met.
  4. Once the market price crosses the trigger threshold, the system automatically places the underlying order (LO/MTL/MP...) onto the order book.
  5. Query the list/details with get_fco_by_*, or cancel with cancel_fco while the order hasn't triggered yet.

Conditional Order Types

TypeMethodDescription
GTDplace_fco_gtdGood-Till-Date order
Stopplace_fco_stopTriggers a market order once price crosses the threshold (Stop Market)
Stop Limitplace_fco_stop_limitTriggers a limit order once price crosses the threshold (Stop Limit)
Trailing Stopplace_fco_trailing_stopFollows the price trend at a fixed trailing amount
Trailing Stop Limitplace_fco_trailing_stop_limitTrailing Stop that places a limit order on trigger
OCOplace_fco_ocoPlaces take-profit (TP) and stop-loss (SL) together — filling one cancels the other
Bull Bearplace_fco_bull_bearCombines an entry order with attached TP/SL

See the full FCOType, FCOOperator, FCOStatus enums and detailed parameters on the Python SDK Enums page.

Sample Code — Sync

python/sample_13_fco_order.py
from datetime import datetime, timedelta

from ssi_sdk import Auth, Trading, Config
from ssi_sdk.enums import FCOOperator, OrderSide
from auth_helper import ensure_auth
from helpers import enum_value

config = Config(
    client_id="<your_client_id>",
    api_key="<your_api_key>",
    api_secret="<your_api_secret>",
    private_key="<your_private_key>",
)
ACCOUNT_NO = "<your_account_no>"
symbol = "SSI"

# Validity window starts from the run time; the server caps it at 31 days (from -> to)
_now = datetime.now()
from_date = _now.strftime("%Y/%m/%d 00:00:00")
to_date = (_now + timedelta(days=30)).strftime("%Y/%m/%d 23:59:59")

# OTP for conditional orders: fill in only if the account requires OTP for FCO, None otherwise
FCO_CODE = None

with Auth(config) as auth:
    ensure_auth(auth, otp="<your_otp>")

    with Trading(auth) as trading:
        # 1. GTD order (Good-Till-Date)
        gtd_res = trading.trading.place_fco_gtd(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.BUY,
            quantity=100, price=26000, price_slip=0,
            from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  GTD: {gtd_res}")

        # 2. Stop order (stop market)
        stop_res = trading.trading.place_fco_stop(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.BUY,
            quantity=100, stop_price=27000,
            operator=FCOOperator.GREATER_OR_EQUAL,
            from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  Stop: {stop_res}")

        # 3. Stop Limit order
        stop_limit_res = trading.trading.place_fco_stop_limit(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.BUY,
            quantity=100, price=27500, price_slip=0, stop_price=27000,
            operator=FCOOperator.GREATER_OR_EQUAL,
            from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  Stop Limit: {stop_limit_res}")

        # 4. Trailing Stop order
        trailing_res = trading.trading.place_fco_trailing_stop(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.SELL,
            quantity=100, active_price=28000, trailing_amount=1000,
            from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  Trailing Stop: {trailing_res}")

        # 5. Trailing Stop Limit order
        trailing_limit_res = trading.trading.place_fco_trailing_stop_limit(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.SELL,
            quantity=100, active_price=28000, trailing_amount=1000,
            price_slip=500, from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  Trailing Stop Limit: {trailing_limit_res}")

        # 6. OCO order (One-Cancels-the-Other)
        oco_res = trading.trading.place_fco_oco(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.SELL,
            quantity=100, tp_active_price=30000, sl_active_price=24000,
            tp_price=30000, sl_price=24000, tp_slip=0, sl_slip=0,
            from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  OCO: {oco_res}")

        # 7. Bull Bear order
        bb_res = trading.trading.place_fco_bull_bear(
            account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.BUY,
            quantity=100, price=26000, price_slip=0,
            tp_active_price=30000, sl_active_price=24000,
            tp_price=30000, sl_price=24000, tp_slip=0, sl_slip=0,
            from_date=from_date, to_date=to_date,
            code=FCO_CODE,
        )
        print(f"  Bull Bear: {bb_res}")

        # 8. Query FCO order list
        fco_list = trading.trading.get_fco_by_account_no(ACCOUNT_NO, page_index=1, page_size=10)
        print(f"  Total FCO orders: {fco_list.items_count}")
        for item in fco_list.fco_list[:5]:
            print(f"  {item.fco_id} | {item.symbol} | {enum_value(item.type)} | {enum_value(item.status)}")

        # 8b. Status history of one FCO order (SDK 3.3.0)
        if getattr(gtd_res, "fco_id", None):
            history = trading.trading.get_fco_status_history(gtd_res.fco_id)
            for h in history:
                print(f"  {h.time} | {h.state} | code={h.code} | {h.detail}")

        # 9. Cancel the FCO order just created (if still WAIT)
        if getattr(gtd_res, "fco_id", None):
            cancel_res = trading.trading.cancel_fco(gtd_res.fco_id, code=FCO_CODE)
            print(f"  Cancel FCO: {cancel_res}")

Sample Code — Async

python/sample_13_fco_order_async.py
import asyncio
from ssi_sdk import AsyncAuth, AsyncTrading, Config
from datetime import datetime, timedelta
from ssi_sdk.enums import FCOOperator, OrderSide
from auth_helper import ensure_auth_async

config = Config(...)
ACCOUNT_NO = "<your_account_no>"
symbol = "SSI"

# Validity window starts from the run time, 31 days max
_now = datetime.now()
from_date = _now.strftime("%Y/%m/%d 00:00:00")
to_date = (_now + timedelta(days=30)).strftime("%Y/%m/%d 23:59:59")
FCO_CODE = None  # OTP for FCO if the account requires it

async def main():
    async with AsyncAuth(config) as auth:
        await ensure_auth_async(auth, otp="<your_otp>")

        async with AsyncTrading(auth) as trading:
            gtd_res = await trading.trading.place_fco_gtd(
                account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.BUY,
                quantity=100, price=26000, price_slip=0,
                from_date=from_date, to_date=to_date,
                code=FCO_CODE,
            )
            print(f"  GTD: {gtd_res}")

            stop_res = await trading.trading.place_fco_stop(
                account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.BUY,
                quantity=100, stop_price=27000,
                operator=FCOOperator.GREATER_OR_EQUAL,
                from_date=from_date, to_date=to_date,
                code=FCO_CODE,
            )
            print(f"  Stop: {stop_res}")

            oco_res = await trading.trading.place_fco_oco(
                account_no=ACCOUNT_NO, symbol=symbol, side=OrderSide.SELL,
                quantity=100, tp_active_price=30000, sl_active_price=24000,
                tp_price=30000, sl_price=24000, tp_slip=0, sl_slip=0,
                from_date=from_date, to_date=to_date,
                code=FCO_CODE,
            )
            print(f"  OCO: {oco_res}")

            fco_list = await trading.trading.get_fco_by_account_no(ACCOUNT_NO, page_index=1, page_size=10)
            print(f"  Total FCO orders: {fco_list.items_count}")

            if getattr(gtd_res, "fco_id", None):
                history = await trading.trading.get_fco_status_history(gtd_res.fco_id)
                for h in history:
                    print(f"  {h.time} | {h.state} | code={h.code} | {h.detail}")
                cancel_res = await trading.trading.cancel_fco(gtd_res.fco_id, code=FCO_CODE)
                print(f"  Cancel FCO: {cancel_res}")

asyncio.run(main())

Notes

  • Validity window: from_date / to_date are computed from the run date (do not hard-code old dates) and the server caps the span from from_date to to_date at 31 days.
  • OTP for FCO: the place_fco_* methods and cancel_fco take an optional code. Fill it in only when the account requires OTP for conditional orders, leave None otherwise; never hard-code an OTP.
  • Status history: get_fco_status_history(fco_id) (SDK 3.3.0) returns the status transitions (time, state, code, detail); an empty list when there are none.
  • An FCO order isn't placed onto the order book immediately — only once its trigger condition is met does the system create the actual underlying order (buying/selling power is checked at that moment).
  • You can only cancel (cancel_fco) an order while it's still waiting (INIT/WAIT), before it has triggered (TRI/TRIT).
  • The price parameter for GTD/Bull Bear can be OrderType.MTL/OrderType.MP to fill at market price immediately on trigger, instead of a fixed price.
  • operator (FCOOperator) sets the comparison condition that triggers Stop/Stop Limit — e.g. GREATER_OR_EQUAL for a buy order waiting for price to rise to the threshold.
  • For OCO/Bull Bear, when one leg (TP or SL) fills, the other leg is automatically cancelled.
  • Track FCO status updates in real time via FCOOrderUpdateMessage in WebSocket Trading.

On this page