Skip to content
All library documents

Lighter Execution Tester for IOC Entry and Post-Only Quotes

Code NautilusTrader

Summary

This example configures a live execution tester for a DOGE perpetual contract on Lighter. At startup, it can open a position with an immediate-or-cancel market order, then maintain post-only limit buy and sell quotes offset from the top of book. It enables reconciliation, subscribes to quotes, and is configured to cancel orders and reduce or close positions when stopped.

The script defaults to the testnet, but dry-run is disabled, so it submits real orders in the configured environment. The example is for checking execution connectivity and order handling, not for generating trading signals; it explicitly says the strategy has no alpha advantage and is not production-ready. Mainnet use may put real funds at risk, while enabling dry-run prevents order submission and shutdown cancel or close commands. The example does not provide performance evidence or a risk-managed trading strategy.

Key ideas

  • The tester opens a position with an immediate-or-cancel market order when configured to do so.
  • It maintains post-only limit quotes on both sides of the book.
  • Reconciliation and quote subscriptions support execution testing against the configured venue.
  • Shutdown settings cancel orders and close or reduce positions.
  • The default environment is testnet, but dry-run is disabled and mainnet use can involve real funds.

Tags

Full text
# exec_tester.py


```py
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
#  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
#  https://nautechsystems.io
#
#  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
#  You may not use this file except in compliance with the License.
#  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
# -------------------------------------------------------------------------------------------------
"""
Test Lighter execution with the built-in ExecTester strategy.

WARNING: Running this script connects to the configured Lighter environment and
places REAL orders immediately. On start it opens a position with an IOC market
order, then maintains post-only limit quotes on both sides of the book. On stop it
cancels all orders and closes all positions. The default target is Lighter Testnet, where no real
funds are at risk; any mainnet deployment uses real funds.
Run only against an account you intend to test. The strategy has no alpha advantage
whatsoever and is not intended for production trading.

Settings are the module-level constants below. Credentials resolve from the namespace selected by
the deployment and environment: `LIGHTER_*`, `LIGHTER_TESTNET_*`, `LIGHTER_ROBINHOOD_*`, or
`LIGHTER_ROBINHOOD_TESTNET_*`.

"""

from __future__ import annotations

from decimal import Decimal

from nautilus_trader.adapters.lighter import LIGHTER
from nautilus_trader.adapters.lighter import LIGHTER_ROBINHOOD
from nautilus_trader.adapters.lighter import LighterDataClientConfig
from nautilus_trader.adapters.lighter import LighterDataClientFactory
from nautilus_trader.adapters.lighter import LighterDeployment
from nautilus_trader.adapters.lighter import LighterEnvironment
from nautilus_trader.adapters.lighter import LighterExecutionClientConfig
from nautilus_trader.adapters.lighter import LighterExecutionClientFactory
from nautilus_trader.common import Environment
from nautilus_trader.config import LiveExecutionEngineConfig
from nautilus_trader.config import LiveRiskEngineConfig
from nautilus_trader.live import LiveNode
from nautilus_trader.model import AccountId
from nautilus_trader.model import ClientId
from nautilus_trader.model import InstrumentId
from nautilus_trader.model import Quantity
from nautilus_trader.model import StrategyId
from nautilus_trader.model import TimeInForce
from nautilus_trader.model import TraderId
from nautilus_trader.testkit import ExecTesterConfig


# WARNING: With DRY_RUN = False, this tester submits orders to the configured
# environment and may use real funds. Set DRY_RUN = True to connect without
# submitting orders or sending shutdown cancel/close commands.
DRY_RUN = False
LIGHTER_ENVIRONMENT = LighterEnvironment.TESTNET
LIGHTER_DEPLOYMENT = LighterDeployment.LIGHTER
VENUE = LIGHTER if LIGHTER_DEPLOYMENT == LighterDeployment.LIGHTER else LIGHTER_ROBINHOOD
TRADER_ID = TraderId.from_str("TESTER-001")
ACCOUNT_ID = AccountId.from_str(f"{VENUE}-001")
STRATEGY_ID = StrategyId.from_str("EXEC_TESTER-001")
INSTRUMENT_ID = InstrumentId.from_str(f"DOGE-PERP.{VENUE}")

ORDER_QTY = "200"  # DOGE contracts, above the 100 contract minimum
OPEN_POSITION_ON_START_QTY = Decimal(ORDER_QTY)
TOB_OFFSET_TICKS = 500


def main() -> None:
    """
    Run the example.
    """
    node = (
        LiveNode.builder("LIGHTER-EXEC-TESTER-001", TRADER_ID, Environment.LIVE)
        .with_reconciliation(reconciliation=True)
        .with_exec_engine_config(
            LiveExecutionEngineConfig(
                reconciliation_lookback_mins=60,
                reconciliation_instrument_ids=[str(INSTRUMENT_ID)],
            ),
        )
        .with_risk_engine_config(LiveRiskEngineConfig(bypass=True))
        .add_data_client(
            VENUE,
            LighterDataClientFactory(),
            LighterDataClientConfig(
                environment=LIGHTER_ENVIRONMENT,
                deployment=LIGHTER_DEPLOYMENT,
            ),
        )
        .add_exec_client(
            VENUE,
            LighterExecutionClientFactory(),
            LighterExecutionClientConfig(
                account_id=ACCOUNT_ID,
                environment=LIGHTER_ENVIRONMENT,
                deployment=LIGHTER_DEPLOYMENT,
            ),
        )
        .build()
    )
    node.add_builtin_strategy(
        "ExecTester",
        ExecTesterConfig(
            strategy_id=STRATEGY_ID,
            instrument_id=INSTRUMENT_ID,
            client_id=ClientId.from_str(VENUE),
            external_order_instrument_ids=[INSTRUMENT_ID],
            order_qty=Quantity.from_str(ORDER_QTY),
            subscribe_quotes=True,
            subscribe_trades=False,
            open_position_on_start_qty=OPEN_POSITION_ON_START_QTY,
            open_position_on_first_quote=True,
            open_position_time_in_force=TimeInForce.IOC,
            enable_limit_buys=True,
            enable_limit_sells=True,
            tob_offset_ticks=TOB_OFFSET_TICKS,
            use_post_only=True,
            cancel_orders_on_stop=True,
            close_positions_on_stop=True,
            reduce_only_on_stop=True,
            dry_run=DRY_RUN,
            log_data=False,
        ),
    )

    node.run()


if __name__ == "__main__":
    main()

```

Shown in full with attribution under the source's licence. Licence: LGPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.