#!/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
For accepted orders and event, I don't have a order_side attr on it.
order = self.cache.order(event.client_order_id)