nautechsystems / nautilus_trader

A high-performance algorithmic trading platform and event-driven backtester
https://nautilustrader.io
GNU Lesser General Public License v3.0
2.14k stars 481 forks source link

AttributeError: 'nautilus_trader.model.orders.limit.LimitOrder' object has no attribute 'order_side'. #1457

Closed x-zho14 closed 9 months ago

x-zho14 commented 9 months ago
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
#  Copyright (C) 2015-2024 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.
# -------------------------------------------------------------------------------------------------

from decimal import Decimal

from nautilus_trader.adapters.binance.common.enums import BinanceAccountType
from nautilus_trader.adapters.binance.config import BinanceDataClientConfig
from nautilus_trader.adapters.binance.config import BinanceExecClientConfig
from nautilus_trader.adapters.binance.factories import BinanceLiveDataClientFactory
from nautilus_trader.adapters.binance.factories import BinanceLiveExecClientFactory
from nautilus_trader.config import InstrumentProviderConfig
from nautilus_trader.config import LiveExecEngineConfig
from nautilus_trader.config import LoggingConfig
from nautilus_trader.config import TradingNodeConfig
from nautilus_trader.config.common import CacheConfig
from nautilus_trader.config.common import DatabaseConfig
from nautilus_trader.live.node import TradingNode
from nautilus_trader.model.data import BarType
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.model.identifiers import TraderId

from nautilus_trader.config import StrategyConfig
from nautilus_trader.core.data import Data
from nautilus_trader.core.message import Event
from nautilus_trader.model.data import Bar
from nautilus_trader.model.data import QuoteTick
from nautilus_trader.model.data import TradeTick
from nautilus_trader.model.instruments import Instrument
from nautilus_trader.trading.strategy import Strategy

from nautilus_trader.model.book import OrderBook
from nautilus_trader.model.data import OrderBookDeltas
from nautilus_trader.model.enums import BookType
from nautilus_trader.model.enums import OrderSide
from nautilus_trader.model.enums import PositionSide
from nautilus_trader.model.events import PositionChanged
from nautilus_trader.model.events import PositionClosed
from nautilus_trader.model.events import PositionOpened
from nautilus_trader.model.objects import Price
import datetime
import time
import threading
import nautilus_trader

class MarketMaker(Strategy):
    """
    Provides a market making strategy for testing.

    Parameters
    ----------
    instrument_id : InstrumentId
        The instrument ID for the strategy.
    trade_size : Decimal
        The position size per trade.
    max_size : Decimal
        The maximum inventory size allowed.

    """

    def __init__(
        self,
        instrument_id: InstrumentId,
        trade_size: Decimal,
        max_size: Decimal,
    ) -> None:
        super().__init__()
        # Configuration
        self.instrument_id = instrument_id
        self.trade_size = trade_size
        self.max_size = max_size
        self.instrument: Instrument | None = None  # Initialized in on_start
        self._book: OrderBook | None = None
        self._grid_price: Decimal | None = 0
        self.sell_init_list = []
        self.buy_init_list = []
        self.sell_locked = False
        self.sell_lock = threading.Lock()
        self.buy_locked = False
        self.buy_lock = threading.Lock()
        self.signed_qty = 0

    def on_start(self) -> None:
        self.instrument = self.cache.instrument(self.instrument_id)
        self.min_precision = Decimal(10**(-self.instrument.price_precision))
        if self.instrument is None:
            self.log.error(f"Could not find instrument for {self.instrument_id}")
            self.stop()
            return

        self._book = OrderBook(
            instrument_id=self.instrument.id,
            book_type=BookType.L2_MBP,
        )

        # self.subscribe_order_book_snapshots(instrument_id=self.instrument_id,depth=20)

        # Subscribe to live data
        self.subscribe_order_book_deltas(self.instrument.id)

        self.subscribe_quote_ticks(self.instrument.id)

        # self.subscribe_order_book_snapshots(
        #     self.instrument_id,
        #     depth=20,
        #     interval_ms=100,
        # ) 

    def on_order_book(self, order_book: OrderBook) -> None:
        """
        Actions to be performed when the strategy is running and receives an order book.

        Parameters
        ----------
        order_book : OrderBook
            The order book received.

        """
        # For debugging (must add a subscription)
        self.log.info(repr(order_book), LogColor.CYAN)

    def on_order_book_deltas(self, deltas: OrderBookDeltas) -> None:
        if not self._book:
            self.log.error("No book being maintained.")
            return
        # for d in deltas.deltas:
            # print((d.ts_init-d.ts_event)/1e6)

        self._book.apply_deltas(deltas)
        # bid_price = self._book.best_bid_price()
        # ask_price = self._book.best_ask_price()

    def on_quote_tick(self, tick: QuoteTick) -> None:

    def on_event(self, event: Event) -> None:
        if isinstance(event, PositionOpened | PositionChanged):
            self.signed_qty = Decimal(event.signed_qty)
            self.log.error("self.signed_qty" + str(self.signed_qty))
        elif isinstance(event, PositionClosed):
            pass

    def on_order_filled(self, event: Event) -> None:

    def on_order_accepted(self, event: Event) -> None:
        print(repr(event), (event.ts_init-event.ts_event)/1e6)
        self.log.error("accepted"+repr(event)+str((event.ts_init-event.ts_event)/1e6))
        order = self.cache.order(event.client_order_id)
        self.log.error("query order"+ repr(order))
        import inspect
        self.log.error("query order"+ repr(order.trader_id))
        self.log.error("query order"+ repr(order.order_type))

        if order.order_side == OrderSide.SELL:
            with self.sell_lock:
                self.sell_locked = False
        if order.order_side == OrderSide.BUY:
            with self.buy_lock:
                self.buy_locked = False
        self.log.info("become sell unlocked")
        self.log.info("become buy unlocked")

    def buy(self, price: Decimal, quantity: Decimal) -> None:
        """
        Users simple buy method (example).
        """
        if not self.instrument:
            self.log.error("No instrument loaded.")
            return

        order = self.order_factory.limit(
            instrument_id=self.instrument_id,
            order_side=OrderSide.BUY,
            price=Price(price, precision=self.instrument.price_precision),
            quantity=self.instrument.make_qty(quantity),
            post_only=True,
        )

        self.submit_order(order)

    def sell(self, price: Decimal, quantity: Decimal) -> None:
        """
        Users simple sell method (example).
        """
        if not self.instrument:
            self.log.error("No instrument loaded.")
            return

        order = self.order_factory.limit(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            price=Price(price, precision=self.instrument.price_precision),
            quantity=self.instrument.make_qty(quantity),
            post_only=True,
        )

        self.submit_order(order)

    def on_stop(self) -> None:
        """
        Actions to be performed when the strategy is stopped.
        """
        self.cancel_all_orders(self.instrument_id)
        self.close_all_positions(self.instrument_id)

# Configure the trading node
config_node = TradingNodeConfig(
    trader_id=TraderId("TESTER-001"),
    logging=LoggingConfig(log_level="INFO"),
    exec_engine=LiveExecEngineConfig(
        reconciliation=True,
        reconciliation_lookback_mins=1440,
    ),
    cache=CacheConfig(
        database=DatabaseConfig(),  # default redis
        buffer_interval_ms=100,
    ),
    data_clients={
        "BINANCE": BinanceDataClientConfig(
            api_key="De7K9FjjFmuqDVhT4gNXydJOHbWIW6uC1MIbdGjw0qCcnS65sgp6atcquuu3euCD",
            api_secret="6GO8oGj4MaJxNSjsXqkZLxVS9ht9MLBlRtQBxm7n9wMaO7nG3ijdBNiUwBTwirrr",
            account_type=BinanceAccountType.USDT_FUTURE,
            instrument_provider=InstrumentProviderConfig(load_all=True),
        ),
    },
    exec_clients={
        "BINANCE": BinanceExecClientConfig(
            api_key="De7K9FjjFmuqDVhT4gNXydJOHbWIW6uC1MIbdGjw0qCcnS65sgp6atcquuu3euCD",
            api_secret="6GO8oGj4MaJxNSjsXqkZLxVS9ht9MLBlRtQBxm7n9wMaO7nG3ijdBNiUwBTwirrr",
            account_type=BinanceAccountType.USDT_FUTURE,
            instrument_provider=InstrumentProviderConfig(load_all=True),
        ),
    }
)

# Instantiate the node with a configuration
node = TradingNode(config=config_node)

# Instantiate your strategy
strategy = MarketMaker(instrument_id=InstrumentId.from_str("TRBUSDT-PERP.BINANCE"), trade_size=Decimal("0.1"), max_size=Decimal("0.1"))
# Add your strategies and modules
node.trader.add_strategy(strategy)

# Register your client factories with the node (can take user defined factories)
node.add_data_client_factory("BINANCE", BinanceLiveDataClientFactory)
node.add_exec_client_factory("BINANCE", BinanceLiveExecClientFactory)
node.build()

# Stop and dispose of the node with SIGINT/CTRL+C
if __name__ == "__main__":
    try:
        node.run()
    finally:
        node.dispose()

For accepted orders and event, I don't have a order_side attr on it.
order = self.cache.order(event.client_order_id)

on_order_accepted File "/home/ubuntu/nautilus_trader/examples/live/binance/binance_futures_market_maker.py", line 196, in on_order_accepted if order.order_side == OrderSide.SELL: ^^^^^^^^^^^^^^^^ AttributeError: 'nautilus_trader.model.orders.limit.LimitOrder' object has no attribute 'order_side'. Did you mean: 'order_type'? (base) ubuntu@ip-172-31-37-9

cjdsellers commented 9 months ago

https://docs.nautilustrader.io/api_reference/model/orders.html#api-reference-model-orders--page-root https://github.com/nautechsystems/nautilus_trader/blob/develop/nautilus_trader/model/orders/base.pxd#L83

side

    The order side.

    Returns :

        OrderSide