From 24a0613076604cea14c6e1762b453dbbb89fd8b6 Mon Sep 17 00:00:00 2001 From: Jeff West Date: Mon, 1 Sep 2025 22:30:56 -0500 Subject: [PATCH] v3.5.7: Fix Decimal JSON serialization in order placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed JSON serialization error when placing orders with Decimal prices - Convert Decimal prices to float for API requests while maintaining internal precision - Added comprehensive documentation examples in Documentation_Examples/ - Updated all version references to v3.5.7 in README, CHANGELOG, and docs - Verified all examples work with the fixed serialization 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .secrets.baseline | 4 +- CHANGELOG.md | 15 +++ README.md | 6 +- docs/changelog.md | 30 +++++ docs/examples/advanced.md | 2 +- docs/examples/realtime.md | 2 +- docs/getting-started/quickstart.md | 107 +++++++++++++----- docs/guide/realtime.md | 2 +- docs/index.md | 10 +- .../Documentation_Examples/quick_start.py | 43 +++++++ .../quick_start/multi_instrument.py | 24 ++++ .../quick_start/multi_instrument_streaming.py | 33 ++++++ .../quick_start/multi_instrument_trading.py | 40 +++++++ .../quick_start/single_instrument.py | 33 ++++++ .../single_instrument_streaming.py | 40 +++++++ .../single_instrument_trading_simple.py | 35 ++++++ pyproject.toml | 2 +- src/project_x_py/__init__.py | 2 +- src/project_x_py/indicators/__init__.py | 2 +- src/project_x_py/order_manager/core.py | 13 ++- tests/order_manager/test_core_advanced.py | 6 +- uv.lock | 2 +- 22 files changed, 406 insertions(+), 47 deletions(-) create mode 100644 examples/Documentation_Examples/quick_start.py create mode 100644 examples/Documentation_Examples/quick_start/multi_instrument.py create mode 100644 examples/Documentation_Examples/quick_start/multi_instrument_streaming.py create mode 100644 examples/Documentation_Examples/quick_start/multi_instrument_trading.py create mode 100644 examples/Documentation_Examples/quick_start/single_instrument.py create mode 100644 examples/Documentation_Examples/quick_start/single_instrument_streaming.py create mode 100644 examples/Documentation_Examples/quick_start/single_instrument_trading_simple.py diff --git a/.secrets.baseline b/.secrets.baseline index 3e58244..a2c738d 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -133,7 +133,7 @@ "filename": "CHANGELOG.md", "hashed_secret": "89a6cfe2a229151e8055abee107d45ed087bbb4f", "is_verified": false, - "line_number": 2183 + "line_number": 2198 } ], "README.md": [ @@ -325,5 +325,5 @@ } ] }, - "generated_at": "2025-09-02T01:57:55Z" + "generated_at": "2025-09-02T03:30:00Z" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 70af97a..b323883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migration guides will be provided for all breaking changes - Semantic versioning (MAJOR.MINOR.PATCH) is strictly followed +## [3.5.7] - 2025-02-02 + +### 🐛 Fixed + +**Order Placement**: +- **Decimal Serialization**: Fixed JSON serialization error when placing orders with Decimal prices +- **API Compatibility**: Ensured all price values are properly converted to float for API requests +- **Price Precision**: Maintained internal Decimal precision while ensuring JSON compatibility + +### 📚 Documentation + +**Examples**: +- **Quick Start Example**: Fixed and verified the quick_start.py example in Documentation_Examples +- **Order Placement**: Ensured all documentation examples work with the fixed serialization + ## [3.5.6] - 2025-02-02 ### 🐛 Fixed diff --git a/README.md b/README.md index e1fb15e..de36284 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ A **high-performance async Python SDK** for the [ProjectX Trading Platform](http This Python SDK acts as a bridge between your trading strategies and the ProjectX platform, handling all the complex API interactions, data processing, and real-time connectivity. -## 🚀 v3.5.6 - Event System & Bracket Order Enhancements +## 🚀 v3.5.7 - Order Placement Serialization Fix -**Latest Version**: v3.5.6 - Critical fixes for multi-instrument event handling and automatic price alignment for bracket orders, ensuring robust real-time trading operations. +**Latest Version**: v3.5.7 - Fixed JSON serialization error when placing orders with Decimal prices, ensuring all price values are properly converted for API requests while maintaining internal precision. **Key Improvements**: - 🔄 **Event Forwarding**: Fixed multi-instrument event propagation with proper bus forwarding @@ -39,7 +39,7 @@ This Python SDK acts as a bridge between your trading strategies and the Project - 🛡️ **Improved Reliability**: 30+ test fixes ensuring production stability - ⚡ **Real-time Fixes**: Corrected bar data access in streaming examples -See [CHANGELOG.md](CHANGELOG.md) for complete v3.5.6 fixes and previous version features. +See [CHANGELOG.md](CHANGELOG.md) for complete v3.5.7 fixes and previous version features. ### 📦 Production Stability Guarantee diff --git a/docs/changelog.md b/docs/changelog.md index f5e4aa4..aa35bd4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,36 @@ All notable changes to the ProjectX Python SDK will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.5.7] - 2025-02-02 + +### 🐛 Fixed + +**Order Placement**: +- **Decimal Serialization**: Fixed JSON serialization error when placing orders with Decimal prices +- **API Compatibility**: Ensured all price values are properly converted to float for API requests +- **Price Precision**: Maintained internal Decimal precision while ensuring JSON compatibility + +### 📚 Documentation + +**Examples**: +- **Quick Start Example**: Fixed and verified the quick_start.py example in Documentation_Examples +- **Order Placement**: Ensured all documentation examples work with the fixed serialization + +## [3.5.6] - 2025-02-02 + +### 🐛 Fixed + +**Multi-Instrument Event System**: +- **Event Forwarding**: Implemented event forwarding from instrument-specific EventBuses to suite-level EventBus +- **InstrumentContext Methods**: Added `on()`, `once()`, `off()`, and `wait_for()` methods that delegate to event_bus +- **Event Propagation**: Fixed broken event system that prevented `mnq_context.wait_for(EventType.NEW_BAR)` from working +- **Multi-Instrument Support**: Events now properly flow from individual instruments to the suite level + +**Bracket Order Improvements**: +- **Automatic Price Alignment**: Changed validation from failing to auto-aligning prices to tick size +- **Smart Adjustment**: Orders with misaligned prices are now automatically corrected instead of rejected +- **Better UX**: Improved user experience by handling price alignment transparently + ## [3.4.0] - 2025-08-28 ### 🚀 New Feature: ETH vs RTH Trading Sessions (Experimental) diff --git a/docs/examples/advanced.md b/docs/examples/advanced.md index fa6ead7..d84d15e 100644 --- a/docs/examples/advanced.md +++ b/docs/examples/advanced.md @@ -1,6 +1,6 @@ # Advanced Trading Examples -This page demonstrates sophisticated trading strategies and advanced features of the ProjectX Python SDK v3.5.6. These examples include order placement with automatic price alignment, risk management, and complex event-driven trading systems with proper multi-instrument event forwarding. +This page demonstrates sophisticated trading strategies and advanced features of the ProjectX Python SDK v3.5.7. These examples include order placement with automatic price alignment, risk management, and complex event-driven trading systems with proper multi-instrument event forwarding. !!! warning "Live Trading Alert" **These examples place REAL ORDERS on the market!** diff --git a/docs/examples/realtime.md b/docs/examples/realtime.md index 5ab0008..903ba35 100644 --- a/docs/examples/realtime.md +++ b/docs/examples/realtime.md @@ -1,6 +1,6 @@ # Real-time Data Processing Examples -This page demonstrates how to work with real-time market data streams using the ProjectX Python SDK v3.5.6. Learn to handle WebSocket data, process multiple timeframes, and build real-time trading systems with the enhanced event system. +This page demonstrates how to work with real-time market data streams using the ProjectX Python SDK v3.5.7. Learn to handle WebSocket data, process multiple timeframes, and build real-time trading systems with the enhanced event system. ## Prerequisites diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 9865a3d..affdac1 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -1,6 +1,6 @@ # Quick Start -This guide will help you get started with the ProjectX Python SDK. +This guide will help you get started with the ProjectX Python SDK. Some examples will place live trades and orders. Please use with caution!! ## Basic Setup @@ -8,20 +8,25 @@ This guide will help you get started with the ProjectX Python SDK. ```python import asyncio + from project_x_py import TradingSuite + async def main(): # Create a trading suite for single instrument suite = await TradingSuite.create( instruments=["MNQ"], # List notation (recommended) timeframes=["1min", "5min"], features=["orderbook"], # Optional features - initial_days=5 # Historical data to load + initial_days=5, # Historical data to load ) # Access the instrument context mnq = suite["MNQ"] + if suite.client.account_info is None: + raise Exception("Account info is None") + # Everything is now connected and ready print(f"Connected to: {suite.client.account_info.name}") @@ -32,6 +37,7 @@ async def main(): # Clean shutdown await suite.disconnect() + # Run the async function asyncio.run(main()) ``` @@ -39,12 +45,17 @@ asyncio.run(main()) ### Multi-Instrument Setup (v3.5.0) ```python +import asyncio + +from project_x_py import TradingSuite + + async def multi_instrument_setup(): # Create suite for multiple instruments suite = await TradingSuite.create( instruments=["MNQ", "ES", "MGC"], # Multiple futures timeframes=["1min", "5min"], - features=["orderbook", "risk_manager"] + features=["orderbook", "risk_manager"], ) print(f"Managing {len(suite)} instruments: {list(suite.keys())}") @@ -56,6 +67,7 @@ async def multi_instrument_setup(): await suite.disconnect() + asyncio.run(multi_instrument_setup()) ``` @@ -64,6 +76,11 @@ asyncio.run(multi_instrument_setup()) ### Single Instrument Trading ```python +import asyncio + +from project_x_py import TradingSuite + + async def trading_example(): suite = await TradingSuite.create(["MNQ"]) # List notation mnq = suite["MNQ"] # Get instrument context @@ -72,31 +89,38 @@ async def trading_example(): order = await mnq.orders.place_market_order( contract_id=mnq.instrument_info.id, side=0, # 0=Buy, 1=Sell - size=1 + size=1, ) - print(f"Order placed: {order.order_id}") + print(f"Order placed: {order.orderId}") # Check position position = await mnq.positions.get_position("MNQ") if position: - print(f"Position: {position.net_position} @ ${position.average_price:,.2f}") + print(f"Position: {position.size} @ ${position.averagePrice:,.2f}") # Place a stop loss - if position and position.net_position > 0: + if position and position.size > 0: stop_order = await mnq.orders.place_stop_order( contract_id=mnq.instrument_info.id, side=1, # Sell - size=position.net_position, - stop_price=position.average_price - 20 # 20 points below entry + size=position.size, + stop_price=position.averagePrice - 20, # 20 points below entry ) - print(f"Stop loss placed: {stop_order.order_id}") + print(f"Stop loss placed: {stop_order.orderId}") await suite.disconnect() + +asyncio.run(trading_example()) ``` ### Multi-Instrument Pairs Trading ```python +import asyncio + +from project_x_py import TradingSuite + + async def pairs_trading_example(): suite = await TradingSuite.create(["ES", "MNQ"]) # S&P 500 vs NASDAQ @@ -107,6 +131,9 @@ async def pairs_trading_example(): es_price = await es_context.data.get_current_price() mnq_price = await mnq_context.data.get_current_price() + if es_price is None or mnq_price is None: + raise Exception("No price data available") + # Calculate spread (normalize by contract values) spread = (es_price * 50) - (mnq_price * 20) print(f"ES/MNQ Spread: ${spread:.2f}") @@ -115,15 +142,20 @@ async def pairs_trading_example(): if spread > 500: # ES expensive relative to MNQ await es_context.orders.place_market_order( contract_id=es_context.instrument_info.id, - side=1, size=1 # Sell ES + side=1, + size=1, # Sell ES ) await mnq_context.orders.place_market_order( contract_id=mnq_context.instrument_info.id, - side=0, size=1 # Buy MNQ + side=0, + size=1, # Buy MNQ ) print("Executed pairs trade: Short ES, Long MNQ") await suite.disconnect() + + +asyncio.run(pairs_trading_example()) ``` ## Real-time Data Streaming @@ -131,26 +163,33 @@ async def pairs_trading_example(): ### Single Instrument Streaming ```python -from project_x_py import EventType +import asyncio + +from project_x_py import EventType, TradingSuite +from project_x_py.event_bus import Event + async def stream_data(): - suite = await TradingSuite.create( - ["MNQ"], - timeframes=["1min", "5min"] - ) + suite = await TradingSuite.create(["MNQ"], timeframes=["15sec", "1min"]) mnq = suite["MNQ"] # Register event handlers - async def on_new_bar(event): + async def on_new_bar(event: Event): data = event.data - timeframe = data.get('timeframe') - bar = data.get('data') + timeframe = data.get("timeframe") + bar = data.get("data") if bar: - print(f"MNQ New {timeframe} bar: ${bar['close']:,.2f} Vol: {bar['volume']:,}") + print( + f"MNQ New {timeframe} bar: ${bar['close']:,.2f} Vol: {bar['volume']:,}" + ) - async def on_quote(event): + async def on_quote(event: Event): quote = event.data + + if quote["bid"] is None or quote["ask"] is None: + return + print(f"MNQ Quote: Bid ${quote['bid']:,.2f} Ask ${quote['ask']:,.2f}") # Subscribe to events for MNQ @@ -161,25 +200,36 @@ async def stream_data(): await asyncio.sleep(30) await suite.disconnect() + + +asyncio.run(stream_data()) ``` ### Multi-Instrument Streaming ```python +import asyncio + +from project_x_py import EventType, TradingSuite +from project_x_py.event_bus import Event + + async def multi_stream_data(): suite = await TradingSuite.create( ["MNQ", "ES", "MGC"], - timeframes=["1min", "5min"] + timeframes=["15sec", "1min"], ) # Register handlers for each instrument for symbol, context in suite.items(): + async def make_handler(sym): - async def on_new_bar(event): + async def on_new_bar(event: Event): data = event.data - if data.get('timeframe') == '5min': - bar = data.get('data') - print(f"{sym} 5min: ${bar['close']:,.2f}") + if data.get("timeframe") == "1min": + bar = data.get("data") + print(f"{sym} 1min: ${bar['close']:,.2f}") + return on_new_bar handler = await make_handler(symbol) @@ -188,6 +238,9 @@ async def multi_stream_data(): # Stream all instruments simultaneously await asyncio.sleep(30) await suite.disconnect() + + +asyncio.run(multi_stream_data()) ``` ## Next Steps diff --git a/docs/guide/realtime.md b/docs/guide/realtime.md index fe8978a..3733ba7 100644 --- a/docs/guide/realtime.md +++ b/docs/guide/realtime.md @@ -1,6 +1,6 @@ # Real-time Data Guide -This guide covers comprehensive real-time data streaming using ProjectX Python SDK v3.5.6+. All real-time operations are fully asynchronous and provide high-performance WebSocket connectivity with automatic reconnection, memory management, and enhanced event forwarding for multi-instrument support. +This guide covers comprehensive real-time data streaming using ProjectX Python SDK v3.5.7+. All real-time operations are fully asynchronous and provide high-performance WebSocket connectivity with automatic reconnection, memory management, and enhanced event forwarding for multi-instrument support. ## Overview diff --git a/docs/index.md b/docs/index.md index e7235bb..cc99f1a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,8 +7,8 @@ **project-x-py** is a high-performance **async Python SDK** for the [ProjectX Trading Platform](https://www.projectx.com/) Gateway API. This library enables developers to build sophisticated trading strategies and applications by providing comprehensive async access to futures trading operations, real-time market data, Level 2 orderbook analysis, and a complete technical analysis suite with 59+ TA-Lib compatible indicators including pattern recognition and chaos theory analysis. -!!! note "Version 3.5.4 - Lorenz Formula Indicator & Test Suite Improvements" - **Latest Release**: Introduces the innovative Lorenz Formula indicator applying chaos theory mathematics to market analysis, providing unique insights into market dynamics through volatility, trend strength, and volume patterns. Also includes comprehensive test suite reorganization for better maintainability and enhanced statistics module coverage. Full backward compatibility maintained with all existing APIs. +!!! note "Version 3.5.7 - Order Placement Serialization Fix" + **Latest Release**: Fixed JSON serialization error when placing orders with Decimal prices. All price values are now properly converted to float for API requests while maintaining internal Decimal precision for accurate tick size alignment. Full backward compatibility maintained with all existing APIs. !!! note "Stable Production Release" Since v3.1.1, this project maintains strict semantic versioning with backward compatibility between minor versions. Breaking changes only occur in major version releases (4.0.0+). Deprecation warnings are provided for at least 2 minor versions before removal. @@ -216,6 +216,12 @@ mnq_context = suite["MNQ"] # Access specific instrument ## Recent Changes +### v3.5.7 - Order Placement Serialization Fix (2025-02-02) +- **Fixed**: JSON serialization error when placing orders with Decimal prices +- **Fixed**: API compatibility by converting Decimal prices to float for JSON requests +- **Fixed**: Documentation examples to work with the corrected serialization +- **Maintained**: Internal Decimal precision for accurate tick size alignment + ### v3.5.3 - Complete Documentation & Testing Improvements (2025-01-31) - **Fixed**: Mypy error with `get_overflow_stats()` method signatures in mmap overflow handling - **Fixed**: Type safety issues in overflow statistics reporting diff --git a/examples/Documentation_Examples/quick_start.py b/examples/Documentation_Examples/quick_start.py new file mode 100644 index 0000000..7cd61a6 --- /dev/null +++ b/examples/Documentation_Examples/quick_start.py @@ -0,0 +1,43 @@ +import asyncio + +from project_x_py import TradingSuite +from project_x_py.indicators import MACD, RSI, SMA + + +async def main(): + # V3.5.4: Multi-instrument TradingSuite with chaos theory indicators + suite = await TradingSuite.create( + instruments=["MNQ", "ES", "MGC"], # Multiple instruments + timeframes=["1min", "5min"], + features=["orderbook", "risk_manager"], + ) + + # Access specific instruments + mnq_context = suite["MNQ"] + es_context = suite["ES"] + + # Get market data with technical analysis + mnq_data = await mnq_context.data.get_data("1min", bars=100) + if mnq_data is None: + raise Exception("No data available") + + mnq_data = mnq_data.pipe(RSI, period=14).pipe(SMA, period=20) + + print(f"MNQ RSI(14): {mnq_data['rsi_14'][-1]:.2f}") + print(f"MNQ SMA(20): {mnq_data['sma_20'][-1]:.2f}") + + # Multi-instrument trading + await mnq_context.orders.place_limit_order( + contract_id=mnq_context.instrument_info.id, side=0, size=1, limit_price=21050.0 + ) + + # Portfolio-level analytics + for symbol, context in suite.items(): + price = await context.data.get_current_price() + print(f"{symbol}: ${price:.2f}") + + await suite.disconnect() + + +# Run the async function +asyncio.run(main()) diff --git a/examples/Documentation_Examples/quick_start/multi_instrument.py b/examples/Documentation_Examples/quick_start/multi_instrument.py new file mode 100644 index 0000000..2f6f00f --- /dev/null +++ b/examples/Documentation_Examples/quick_start/multi_instrument.py @@ -0,0 +1,24 @@ +import asyncio + +from project_x_py import TradingSuite + + +async def multi_instrument_setup(): + # Create suite for multiple instruments + suite = await TradingSuite.create( + instruments=["MNQ", "ES", "MGC"], # Multiple futures + timeframes=["1min", "5min"], + features=["orderbook", "risk_manager"], + ) + + print(f"Managing {len(suite)} instruments: {list(suite.keys())}") + + # Access individual instruments + for symbol, context in suite.items(): + current_price = await context.data.get_current_price() + print(f"{symbol}: ${current_price:,.2f}") + + await suite.disconnect() + + +asyncio.run(multi_instrument_setup()) diff --git a/examples/Documentation_Examples/quick_start/multi_instrument_streaming.py b/examples/Documentation_Examples/quick_start/multi_instrument_streaming.py new file mode 100644 index 0000000..f7585c7 --- /dev/null +++ b/examples/Documentation_Examples/quick_start/multi_instrument_streaming.py @@ -0,0 +1,33 @@ +import asyncio + +from project_x_py import EventType, TradingSuite +from project_x_py.event_bus import Event + + +async def multi_stream_data(): + suite = await TradingSuite.create( + ["MNQ", "ES", "MGC"], + timeframes=["15sec", "1min"], + ) + + # Register handlers for each instrument + for symbol, context in suite.items(): + + async def make_handler(sym): + async def on_new_bar(event: Event): + data = event.data + if data.get("timeframe") == "1min": + bar = data.get("data") + print(f"{sym} 1min: ${bar['close']:,.2f}") + + return on_new_bar + + handler = await make_handler(symbol) + await context.on(EventType.NEW_BAR, handler) + + # Stream all instruments simultaneously + await asyncio.sleep(30) + await suite.disconnect() + + +asyncio.run(multi_stream_data()) diff --git a/examples/Documentation_Examples/quick_start/multi_instrument_trading.py b/examples/Documentation_Examples/quick_start/multi_instrument_trading.py new file mode 100644 index 0000000..7ebd1ab --- /dev/null +++ b/examples/Documentation_Examples/quick_start/multi_instrument_trading.py @@ -0,0 +1,40 @@ +import asyncio + +from project_x_py import TradingSuite + + +async def pairs_trading_example(): + suite = await TradingSuite.create(["ES", "MNQ"]) # S&P 500 vs NASDAQ + + es_context = suite["ES"] + mnq_context = suite["MNQ"] + + # Get current prices + es_price = await es_context.data.get_current_price() + mnq_price = await mnq_context.data.get_current_price() + + if es_price is None or mnq_price is None: + raise Exception("No price data available") + + # Calculate spread (normalize by contract values) + spread = (es_price * 50) - (mnq_price * 20) + print(f"ES/MNQ Spread: ${spread:.2f}") + + # Simple spread trading logic + if spread > 500: # ES expensive relative to MNQ + await es_context.orders.place_market_order( + contract_id=es_context.instrument_info.id, + side=1, + size=1, # Sell ES + ) + await mnq_context.orders.place_market_order( + contract_id=mnq_context.instrument_info.id, + side=0, + size=1, # Buy MNQ + ) + print("Executed pairs trade: Short ES, Long MNQ") + + await suite.disconnect() + + +asyncio.run(pairs_trading_example()) diff --git a/examples/Documentation_Examples/quick_start/single_instrument.py b/examples/Documentation_Examples/quick_start/single_instrument.py new file mode 100644 index 0000000..92bf349 --- /dev/null +++ b/examples/Documentation_Examples/quick_start/single_instrument.py @@ -0,0 +1,33 @@ +import asyncio + +from project_x_py import TradingSuite + + +async def main(): + # Create a trading suite for single instrument + suite = await TradingSuite.create( + instruments=["MNQ"], # List notation (recommended) + timeframes=["1min", "5min"], + features=["orderbook"], # Optional features + initial_days=5, # Historical data to load + ) + + # Access the instrument context + mnq = suite["MNQ"] + + if suite.client.account_info is None: + raise Exception("Account info is None") + + # Everything is now connected and ready + print(f"Connected to: {suite.client.account_info.name}") + + # Access current market data + current_price = await mnq.data.get_current_price() + print(f"MNQ Current price: ${current_price:,.2f}") + + # Clean shutdown + await suite.disconnect() + + +# Run the async function +asyncio.run(main()) diff --git a/examples/Documentation_Examples/quick_start/single_instrument_streaming.py b/examples/Documentation_Examples/quick_start/single_instrument_streaming.py new file mode 100644 index 0000000..9a0b064 --- /dev/null +++ b/examples/Documentation_Examples/quick_start/single_instrument_streaming.py @@ -0,0 +1,40 @@ +import asyncio + +from project_x_py import EventType, TradingSuite +from project_x_py.event_bus import Event + + +async def stream_data(): + suite = await TradingSuite.create(["MNQ"], timeframes=["15sec", "1min"]) + + mnq = suite["MNQ"] + + # Register event handlers + async def on_new_bar(event: Event): + data = event.data + timeframe = data.get("timeframe") + bar = data.get("data") + if bar: + print( + f"MNQ New {timeframe} bar: ${bar['close']:,.2f} Vol: {bar['volume']:,}" + ) + + async def on_quote(event: Event): + quote = event.data + + if quote["bid"] is None or quote["ask"] is None: + return + + print(f"MNQ Quote: Bid ${quote['bid']:,.2f} Ask ${quote['ask']:,.2f}") + + # Subscribe to events for MNQ + await mnq.on(EventType.NEW_BAR, on_new_bar) + await mnq.on(EventType.QUOTE_UPDATE, on_quote) + + # Keep streaming for 30 seconds + await asyncio.sleep(30) + + await suite.disconnect() + + +asyncio.run(stream_data()) diff --git a/examples/Documentation_Examples/quick_start/single_instrument_trading_simple.py b/examples/Documentation_Examples/quick_start/single_instrument_trading_simple.py new file mode 100644 index 0000000..a975032 --- /dev/null +++ b/examples/Documentation_Examples/quick_start/single_instrument_trading_simple.py @@ -0,0 +1,35 @@ +import asyncio + +from project_x_py import TradingSuite + + +async def trading_example(): + suite = await TradingSuite.create(["MNQ"]) # List notation + mnq = suite["MNQ"] # Get instrument context + + # Place a market order + order = await mnq.orders.place_market_order( + contract_id=mnq.instrument_info.id, + side=0, # 0=Buy, 1=Sell + size=1, + ) + print(f"Order placed: {order.orderId}") + + # Check position + position = await mnq.positions.get_position("MNQ") + if position: + print(f"Position: {position.size} @ ${position.averagePrice:,.2f}") + + # Place a stop loss + if position and position.size > 0: + stop_order = await mnq.orders.place_stop_order( + contract_id=mnq.instrument_info.id, + side=1, # Sell + size=position.size, + stop_price=position.averagePrice - 20, # 20 points below entry + ) + print(f"Stop loss placed: {stop_order.orderId}") + + await suite.disconnect() + +asyncio.run(trading_example()) diff --git a/pyproject.toml b/pyproject.toml index b622d54..39d3ee9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "project-x-py" -version = "3.5.6" +version = "3.5.7" description = "High-performance Python SDK for futures trading with real-time WebSocket data, technical indicators, order management, and market depth analysis" readme = "README.md" license = { text = "MIT" } diff --git a/src/project_x_py/__init__.py b/src/project_x_py/__init__.py index d831fd0..4ae249c 100644 --- a/src/project_x_py/__init__.py +++ b/src/project_x_py/__init__.py @@ -109,7 +109,7 @@ - `utils`: Utility functions and calculations """ -__version__ = "3.5.6" +__version__ = "3.5.7" __author__ = "TexasCoding" # Core client classes - renamed from Async* to standard names diff --git a/src/project_x_py/indicators/__init__.py b/src/project_x_py/indicators/__init__.py index b3713eb..0074104 100644 --- a/src/project_x_py/indicators/__init__.py +++ b/src/project_x_py/indicators/__init__.py @@ -207,7 +207,7 @@ ) # Version info -__version__ = "3.5.6" +__version__ = "3.5.7" __author__ = "TexasCoding" diff --git a/src/project_x_py/order_manager/core.py b/src/project_x_py/order_manager/core.py index 73dbabb..bfe12a8 100644 --- a/src/project_x_py/order_manager/core.py +++ b/src/project_x_py/order_manager/core.py @@ -519,15 +519,22 @@ async def place_order( ) # Build order request payload + # Convert Decimal prices to float for JSON serialization payload = { "accountId": account_id, "contractId": contract_id, "type": order_type, "side": side, "size": size, - "limitPrice": aligned_limit_price, - "stopPrice": aligned_stop_price, - "trailPrice": aligned_trail_price, + "limitPrice": float(aligned_limit_price) + if aligned_limit_price is not None + else None, + "stopPrice": float(aligned_stop_price) + if aligned_stop_price is not None + else None, + "trailPrice": float(aligned_trail_price) + if aligned_trail_price is not None + else None, "linkedOrderId": linked_order_id, } diff --git a/tests/order_manager/test_core_advanced.py b/tests/order_manager/test_core_advanced.py index 808188f..8324470 100644 --- a/tests/order_manager/test_core_advanced.py +++ b/tests/order_manager/test_core_advanced.py @@ -250,9 +250,9 @@ async def test_place_order_aligns_all_price_types(self, order_manager): call_args = order_manager.project_x._make_request.call_args[1]["data"] # Prices are now Decimal objects for precision from decimal import Decimal - assert call_args.get("limitPrice") == Decimal('100.78') - assert call_args.get("stopPrice") == Decimal('99.33') - assert call_args.get("trailPrice") == Decimal('2.0') + assert call_args.get("limitPrice") == float(Decimal('100.78')) + assert call_args.get("stopPrice") == float(Decimal('99.33')) + assert call_args.get("trailPrice") == float(Decimal('2.0')) class TestConcurrentOrderOperations: diff --git a/uv.lock b/uv.lock index c580e72..f084171 100644 --- a/uv.lock +++ b/uv.lock @@ -2360,7 +2360,7 @@ wheels = [ [[package]] name = "project-x-py" -version = "3.5.6" +version = "3.5.7" source = { editable = "." } dependencies = [ { name = "cachetools" },