Skip to content

Commit 4014f13

Browse files
committed
tweaked imbalance and cumulative delta
1 parent f03316f commit 4014f13

File tree

8 files changed

+92
-67
lines changed

8 files changed

+92
-67
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ A **high-performance Python client** for the TopStepX ProjectX Gateway API, desi
99

1010
## 📊 Project Status
1111

12-
**Current Version**: v1.0.13 (Enhanced with Complete TA-Lib Overlap Indicators)
12+
**Current Version**: v1.0.14 (Enhanced with Complete TA-Lib Overlap Indicators)
1313

1414
**Production Ready Features**:
1515
- Complete futures trading API integration with connection pooling
@@ -594,7 +594,7 @@ We welcome contributions! Please follow these guidelines:
594594

595595
## 📝 Changelog
596596

597-
### Version 1.0.13 (Latest)
597+
### Version 1.0.14 (Latest)
598598
**🔄 Order-Position Synchronization & Enhanced Testing**
599599
-**Order-Position Sync**: Automatic synchronization between orders and positions
600600
-**Position Order Tracking**: Orders automatically tracked and associated with positions

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
project = "project-x-py"
2525
copyright = "2025, Jeff West"
2626
author = "Jeff West"
27-
release = "1.0.13"
28-
version = "1.0.13"
27+
release = "1.0.14"
28+
version = "1.0.14"
2929

3030
# -- General configuration ---------------------------------------------------
3131

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "project-x-py"
3-
version = "1.0.13"
3+
version = "1.0.14"
44
description = "Professional Python client for TopStepX ProjectX Gateway API - futures trading, real-time data, and market analysis"
55
readme = "README.md"
66
license = { text = "MIT" }

scripts/build.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Usage:
66
python scripts/build.py
77
# Or with uv
8-
uv run scripts/build.py
8+
uv run scripts/build.pys
99
"""
1010

1111
import subprocess
@@ -17,7 +17,9 @@ def run_command(cmd: str, description: str) -> bool:
1717
"""Run a command and return success status."""
1818
print(f"🔄 {description}...")
1919
try:
20-
result = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True)
20+
result = subprocess.run(
21+
cmd, shell=True, check=True, capture_output=True, text=True
22+
)
2123
if result.stdout.strip():
2224
print(result.stdout)
2325
return True
@@ -31,32 +33,35 @@ def run_command(cmd: str, description: str) -> bool:
3133
def main():
3234
"""Main build process with version synchronization."""
3335
print("🚀 Building project-x-py with version synchronization...")
34-
36+
3537
# Ensure we're in the project root
3638
project_root = Path(__file__).parent.parent
3739
original_cwd = Path.cwd()
3840
try:
3941
import os
42+
4043
os.chdir(project_root)
41-
44+
4245
# Step 1: Sync versions
4346
if not run_command("python scripts/version_sync.py", "Synchronizing versions"):
4447
return 1
45-
48+
4649
# Step 2: Clean previous builds
47-
if not run_command("rm -rf dist/ build/ *.egg-info/", "Cleaning build artifacts"):
50+
if not run_command(
51+
"rm -rf dist/ build/ *.egg-info/", "Cleaning build artifacts"
52+
):
4853
print("⚠️ Clean failed, continuing...")
49-
54+
5055
# Step 3: Build package
5156
if not run_command("uv build", "Building package"):
5257
return 1
53-
58+
5459
print("✅ Build complete! Package ready in dist/")
5560
return 0
56-
61+
5762
finally:
5863
os.chdir(original_cwd)
5964

6065

6166
if __name__ == "__main__":
62-
sys.exit(main())
67+
sys.exit(main())

scripts/version_sync.py

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""
33
Version Synchronization Script for project-x-py
44
5-
This script ensures all version references are synchronized with the single
5+
This script ensures all version references are synchronized with the single
66
source of truth in src/project_x_py/__init__.py before building.
77
88
Usage:
@@ -21,12 +21,12 @@ def get_version_from_init():
2121
init_file = Path("src/project_x_py/__init__.py")
2222
if not init_file.exists():
2323
raise FileNotFoundError(f"Cannot find {init_file}")
24-
24+
2525
content = init_file.read_text()
2626
match = re.search(r'__version__ = ["\']([^"\']+)["\']', content)
2727
if not match:
2828
raise ValueError("Cannot find __version__ in __init__.py")
29-
29+
3030
return match.group(1)
3131

3232

@@ -36,15 +36,15 @@ def update_pyproject_toml(version):
3636
if not pyproject_file.exists():
3737
print("⚠️ pyproject.toml not found, skipping")
3838
return False
39-
39+
4040
content = pyproject_file.read_text()
4141
new_content = re.sub(
4242
r'^version = ["\'][^"\']+["\']',
4343
f'version = "{version}"',
4444
content,
45-
flags=re.MULTILINE
45+
flags=re.MULTILINE,
4646
)
47-
47+
4848
if content != new_content:
4949
pyproject_file.write_text(new_content)
5050
print(f"✅ Updated pyproject.toml to version {version}")
@@ -60,14 +60,12 @@ def update_indicators_init(version):
6060
if not indicators_file.exists():
6161
print("⚠️ indicators/__init__.py not found, skipping")
6262
return False
63-
63+
6464
content = indicators_file.read_text()
6565
new_content = re.sub(
66-
r'__version__ = ["\'][^"\']+["\']',
67-
f'__version__ = "{version}"',
68-
content
66+
r'__version__ = ["\'][^"\']+["\']', f'__version__ = "{version}"', content
6967
)
70-
68+
7169
if content != new_content:
7270
indicators_file.write_text(new_content)
7371
print(f"✅ Updated indicators/__init__.py to version {version}")
@@ -83,31 +81,27 @@ def update_readme(version):
8381
if not readme_file.exists():
8482
print("⚠️ README.md not found, skipping")
8583
return False
86-
84+
8785
content = readme_file.read_text()
8886
changes_made = False
89-
87+
9088
# Update "Current Version" section
9189
new_content = re.sub(
92-
r'\*\*Current Version\*\*: v[\d.]+',
93-
f'**Current Version**: v{version}',
94-
content
90+
r"\*\*Current Version\*\*: v[\d.]+", f"**Current Version**: v{version}", content
9591
)
96-
92+
9793
# Update changelog section
9894
new_content = re.sub(
99-
r'### Version [\d.]+ \(Latest\)',
100-
f'### Version {version} (Latest)',
101-
new_content
95+
r"### Version [\d.]+ \(Latest\)", f"### Version {version} (Latest)", new_content
10296
)
103-
97+
10498
if content != new_content:
10599
readme_file.write_text(new_content)
106100
print(f"✅ Updated README.md to version {version}")
107101
changes_made = True
108102
else:
109103
print(f"✓ README.md already at version {version}")
110-
104+
111105
return changes_made
112106

113107

@@ -117,19 +111,15 @@ def update_docs_conf(version):
117111
if not conf_file.exists():
118112
print("⚠️ docs/conf.py not found, skipping")
119113
return False
120-
114+
121115
content = conf_file.read_text()
122116
new_content = re.sub(
123-
r'release = ["\'][^"\']+["\']',
124-
f'release = "{version}"',
125-
content
117+
r'release = ["\'][^"\']+["\']', f'release = "{version}"', content
126118
)
127119
new_content = re.sub(
128-
r'version = ["\'][^"\']+["\']',
129-
f'version = "{version}"',
130-
new_content
120+
r'version = ["\'][^"\']+["\']', f'version = "{version}"', new_content
131121
)
132-
122+
133123
if content != new_content:
134124
conf_file.write_text(new_content)
135125
print(f"✅ Updated docs/conf.py to version {version}")
@@ -142,32 +132,32 @@ def update_docs_conf(version):
142132
def main():
143133
"""Main synchronization process."""
144134
print("🔄 Synchronizing version numbers...")
145-
135+
146136
try:
147137
# Get the authoritative version
148138
version = get_version_from_init()
149139
print(f"📋 Source version: {version}")
150-
140+
151141
# Update all files
152142
changes = []
153143
changes.append(update_pyproject_toml(version))
154144
changes.append(update_indicators_init(version))
155145
changes.append(update_readme(version))
156146
changes.append(update_docs_conf(version))
157-
147+
158148
# Summary
159149
if any(changes):
160150
print(f"\n✅ Version synchronization complete! All files now at v{version}")
161151
print(" Ready for: uv build")
162152
else:
163153
print(f"\n✓ All files already synchronized at v{version}")
164-
154+
165155
return 0
166-
156+
167157
except Exception as e:
168158
print(f"\n❌ Error during version sync: {e}")
169159
return 1
170160

171161

172162
if __name__ == "__main__":
173-
sys.exit(main())
163+
sys.exit(main())

src/project_x_py/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from typing import Any, Optional
1717

18-
__version__ = "1.0.13"
18+
__version__ = "1.0.14"
1919
__author__ = "TexasCoding"
2020

2121
# Core client classes

src/project_x_py/indicators/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
)
133133

134134
# Version info
135-
__version__ = "1.0.13"
135+
__version__ = "1.0.14"
136136
__author__ = "TexasCoding"
137137

138138

src/project_x_py/orderbook.py

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,14 +1655,14 @@ def get_cumulative_delta(self, time_window_minutes: int = 30) -> dict[str, Any]:
16551655
}
16561656
)
16571657

1658-
# Determine trend
1659-
if cumulative_delta > 500:
1658+
# Determine trend with wider, less sensitive thresholds
1659+
if cumulative_delta > 5000:
16601660
trend = "strongly_bullish"
1661-
elif cumulative_delta > 100:
1661+
elif cumulative_delta > 1000:
16621662
trend = "bullish"
1663-
elif cumulative_delta < -500:
1663+
elif cumulative_delta < -5000:
16641664
trend = "strongly_bearish"
1665-
elif cumulative_delta < -100:
1665+
elif cumulative_delta < -1000:
16661666
trend = "bearish"
16671667
else:
16681668
trend = "neutral"
@@ -1687,7 +1687,7 @@ def get_cumulative_delta(self, time_window_minutes: int = 30) -> dict[str, Any]:
16871687
self.logger.error(f"Error calculating cumulative delta: {e}")
16881688
return {"cumulative_delta": 0, "error": str(e)}
16891689

1690-
def get_market_imbalance(self) -> dict[str, Any]:
1690+
def get_market_imbalance(self, levels: int = 10) -> dict[str, Any]:
16911691
"""
16921692
Calculate market imbalance metrics from orderbook and trade flow.
16931693
@@ -1697,8 +1697,8 @@ def get_market_imbalance(self) -> dict[str, Any]:
16971697
try:
16981698
with self.orderbook_lock:
16991699
# Get top 10 levels for analysis
1700-
bids = self.get_orderbook_bids(10)
1701-
asks = self.get_orderbook_asks(10)
1700+
bids = self.get_orderbook_bids(levels)
1701+
asks = self.get_orderbook_asks(levels)
17021702

17031703
if len(bids) == 0 or len(asks) == 0:
17041704
return {
@@ -1742,17 +1742,32 @@ def get_market_imbalance(self) -> dict[str, Any]:
17421742
) / trade_flow["total_volume"]
17431743

17441744
# Determine direction and confidence
1745-
# 🧪 TEMPORARY: Lower thresholds for low-volatility debugging
1746-
# Normal: 0.3, Debug: 0.05 (much more sensitive)
1747-
bullish_threshold = 0.05 # Was 0.3
1748-
bearish_threshold = -0.05 # Was -0.3
1745+
# Production thresholds for better signal quality
1746+
bullish_threshold = (
1747+
0.15 # Moderate threshold (was 0.05 debug, 0.3 original)
1748+
)
1749+
bearish_threshold = (
1750+
-0.15
1751+
) # Moderate threshold (was -0.05 debug, -0.3 original)
17491752

17501753
if imbalance_ratio > bullish_threshold:
17511754
direction = "bullish"
1752-
confidence = "high" if trade_imbalance > 0.2 else "medium"
1755+
# Enhanced confidence logic
1756+
if imbalance_ratio > 0.25 and trade_imbalance > 0.2:
1757+
confidence = "high"
1758+
elif imbalance_ratio > 0.2 or trade_imbalance > 0.15:
1759+
confidence = "medium"
1760+
else:
1761+
confidence = "low"
17531762
elif imbalance_ratio < bearish_threshold:
17541763
direction = "bearish"
1755-
confidence = "high" if trade_imbalance < -0.2 else "medium"
1764+
# Enhanced confidence logic
1765+
if imbalance_ratio < -0.25 and trade_imbalance < -0.2:
1766+
confidence = "high"
1767+
elif imbalance_ratio < -0.2 or trade_imbalance < -0.15:
1768+
confidence = "medium"
1769+
else:
1770+
confidence = "low"
17561771
else:
17571772
direction = "neutral"
17581773
confidence = "low"
@@ -1767,12 +1782,27 @@ def get_market_imbalance(self) -> dict[str, Any]:
17671782
"bid_ask_ratio": top_bid_volume / top_ask_volume
17681783
if top_ask_volume > 0
17691784
else float("inf"),
1785+
"volume_concentration": (top_bid_volume + top_ask_volume)
1786+
/ (
1787+
bids.select(pl.col("volume").sum()).item()
1788+
+ asks.select(pl.col("volume").sum()).item()
1789+
)
1790+
if (
1791+
bids.select(pl.col("volume").sum()).item()
1792+
+ asks.select(pl.col("volume").sum()).item()
1793+
)
1794+
> 0
1795+
else 0,
17701796
},
17711797
"trade_flow_metrics": {
17721798
"trade_imbalance": trade_imbalance,
17731799
"recent_buy_volume": trade_flow["buy_volume"],
17741800
"recent_sell_volume": trade_flow["sell_volume"],
1801+
"buy_sell_ratio": trade_flow.get("buy_sell_ratio", 0),
1802+
"trade_count": trade_flow.get("trade_count", 0),
17751803
},
1804+
"signal_strength": abs(imbalance_ratio),
1805+
"timestamp": datetime.now(self.timezone),
17761806
}
17771807

17781808
except Exception as e:

0 commit comments

Comments
 (0)