tradepose-models 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tradepose_models/__init__.py +44 -0
- tradepose_models/auth/__init__.py +13 -0
- tradepose_models/auth/api_keys.py +52 -0
- tradepose_models/auth/auth.py +20 -0
- tradepose_models/base.py +57 -0
- tradepose_models/billing/__init__.py +33 -0
- tradepose_models/billing/checkout.py +17 -0
- tradepose_models/billing/plans.py +32 -0
- tradepose_models/billing/subscriptions.py +34 -0
- tradepose_models/billing/usage.py +71 -0
- tradepose_models/broker/__init__.py +34 -0
- tradepose_models/broker/account_config.py +93 -0
- tradepose_models/broker/account_models.py +61 -0
- tradepose_models/broker/binding.py +54 -0
- tradepose_models/broker/connection_status.py +14 -0
- tradepose_models/commands/__init__.py +8 -0
- tradepose_models/commands/trader_command.py +80 -0
- tradepose_models/datafeed/__init__.py +19 -0
- tradepose_models/datafeed/events.py +132 -0
- tradepose_models/enums/__init__.py +47 -0
- tradepose_models/enums/account_source.py +42 -0
- tradepose_models/enums/broker_type.py +21 -0
- tradepose_models/enums/currency.py +17 -0
- tradepose_models/enums/engagement_phase.py +47 -0
- tradepose_models/enums/execution_mode.py +16 -0
- tradepose_models/enums/export_type.py +23 -0
- tradepose_models/enums/freq.py +32 -0
- tradepose_models/enums/indicator_type.py +46 -0
- tradepose_models/enums/operation_type.py +19 -0
- tradepose_models/enums/order_strategy.py +47 -0
- tradepose_models/enums/orderbook_event_type.py +29 -0
- tradepose_models/enums/persist_mode.py +28 -0
- tradepose_models/enums/stream.py +14 -0
- tradepose_models/enums/task_status.py +23 -0
- tradepose_models/enums/trade_direction.py +42 -0
- tradepose_models/enums/trend_type.py +22 -0
- tradepose_models/enums/weekday.py +30 -0
- tradepose_models/enums.py +32 -0
- tradepose_models/events/__init__.py +11 -0
- tradepose_models/events/order_events.py +79 -0
- tradepose_models/export/__init__.py +19 -0
- tradepose_models/export/request.py +52 -0
- tradepose_models/export/requests.py +75 -0
- tradepose_models/export/task_metadata.py +97 -0
- tradepose_models/gateway/__init__.py +19 -0
- tradepose_models/gateway/responses.py +37 -0
- tradepose_models/indicators/__init__.py +56 -0
- tradepose_models/indicators/base.py +42 -0
- tradepose_models/indicators/factory.py +254 -0
- tradepose_models/indicators/market_profile.md +60 -0
- tradepose_models/indicators/market_profile.py +333 -0
- tradepose_models/indicators/market_profile_developer.md +1782 -0
- tradepose_models/indicators/market_profile_trading.md +1060 -0
- tradepose_models/indicators/momentum.py +53 -0
- tradepose_models/indicators/moving_average.py +63 -0
- tradepose_models/indicators/other.py +40 -0
- tradepose_models/indicators/trend.py +80 -0
- tradepose_models/indicators/volatility.py +57 -0
- tradepose_models/instruments/__init__.py +13 -0
- tradepose_models/instruments/instrument.py +87 -0
- tradepose_models/scheduler/__init__.py +9 -0
- tradepose_models/scheduler/results.py +49 -0
- tradepose_models/schemas/__init__.py +15 -0
- tradepose_models/schemas/enhanced_ohlcv.py +111 -0
- tradepose_models/schemas/performance.py +40 -0
- tradepose_models/schemas/trades.py +64 -0
- tradepose_models/schemas.py +34 -0
- tradepose_models/shared.py +15 -0
- tradepose_models/strategy/__init__.py +52 -0
- tradepose_models/strategy/base.py +56 -0
- tradepose_models/strategy/blueprint.py +55 -0
- tradepose_models/strategy/config.py +142 -0
- tradepose_models/strategy/entities.py +104 -0
- tradepose_models/strategy/helpers.py +173 -0
- tradepose_models/strategy/indicator_spec.py +531 -0
- tradepose_models/strategy/performance.py +66 -0
- tradepose_models/strategy/portfolio.py +171 -0
- tradepose_models/strategy/registry.py +249 -0
- tradepose_models/strategy/requests.py +33 -0
- tradepose_models/strategy/trigger.py +77 -0
- tradepose_models/trading/__init__.py +55 -0
- tradepose_models/trading/engagement.py +160 -0
- tradepose_models/trading/orderbook.py +73 -0
- tradepose_models/trading/orders.py +137 -0
- tradepose_models/trading/positions.py +78 -0
- tradepose_models/trading/trader_commands.py +138 -0
- tradepose_models/trading/trades_execution.py +27 -0
- tradepose_models/types.py +35 -0
- tradepose_models/utils/__init__.py +13 -0
- tradepose_models/utils/rate_converter.py +112 -0
- tradepose_models/validators.py +32 -0
- tradepose_models-1.1.0.dist-info/METADATA +633 -0
- tradepose_models-1.1.0.dist-info/RECORD +94 -0
- tradepose_models-1.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Engagement Model (Issue #305, #320)
|
|
3
|
+
|
|
4
|
+
Provides the Engagement model for trade execution context.
|
|
5
|
+
Tracks the lifecycle of a trade from signal to completion.
|
|
6
|
+
|
|
7
|
+
8-Phase lifecycle:
|
|
8
|
+
PENDING(0) → ENTERING(1) → HOLDING(2) → EXITING(3) → CLOSED(4)
|
|
9
|
+
│ │
|
|
10
|
+
▼ ▼
|
|
11
|
+
FAILED(5) EXIT_FAILED(7)
|
|
12
|
+
|
|
13
|
+
CANCELLED(6) - Signal cancelled before execution
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from decimal import Decimal
|
|
18
|
+
from typing import Optional
|
|
19
|
+
from uuid import UUID
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel, Field
|
|
22
|
+
|
|
23
|
+
from ..enums import EngagementPhase, TradeDirection
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Engagement(BaseModel):
|
|
27
|
+
"""Trade execution context (Engagement).
|
|
28
|
+
|
|
29
|
+
Represents the binding between a trade signal and its execution.
|
|
30
|
+
One engagement can generate multiple orders (entry, SL, TP, exit).
|
|
31
|
+
|
|
32
|
+
Unique constraint: (user_id, account_id, binding_id, portfolio_id, blueprint_id, trade_id)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
id: UUID = Field(..., description="Engagement UUID (primary key)")
|
|
36
|
+
user_id: UUID = Field(..., description="User UUID")
|
|
37
|
+
account_id: UUID = Field(..., description="Trading account UUID")
|
|
38
|
+
binding_id: UUID = Field(..., description="Account-Portfolio binding UUID")
|
|
39
|
+
portfolio_id: UUID = Field(..., description="Portfolio UUID")
|
|
40
|
+
blueprint_id: UUID = Field(..., description="Blueprint UUID")
|
|
41
|
+
trade_id: UUID = Field(..., description="Trade UUID (FK to trades)")
|
|
42
|
+
|
|
43
|
+
# FK fields for direct lookup
|
|
44
|
+
strategy_id: Optional[UUID] = Field(None, description="Strategy UUID")
|
|
45
|
+
portfolio_allocation_id: Optional[UUID] = Field(None, description="Portfolio allocation UUID")
|
|
46
|
+
|
|
47
|
+
# Phase system (8 states) - Issue #305
|
|
48
|
+
phase: EngagementPhase = Field(
|
|
49
|
+
default=EngagementPhase.PENDING,
|
|
50
|
+
description="Engagement phase (0-7): PENDING, ENTERING, HOLDING, EXITING, CLOSED, FAILED, CANCELLED, EXIT_FAILED",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Latest tracking
|
|
54
|
+
is_latest: bool = Field(
|
|
55
|
+
default=True,
|
|
56
|
+
description="True if this is the most recent engagement for this trade context",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Position sizing (MAE-based calculation)
|
|
60
|
+
target_quantity: Optional[Decimal] = Field(
|
|
61
|
+
None,
|
|
62
|
+
description="Calculated position size based on MAE formula",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Trade direction and prices
|
|
66
|
+
direction: Optional[TradeDirection] = Field(
|
|
67
|
+
None,
|
|
68
|
+
description="Trade direction: Long or Short",
|
|
69
|
+
)
|
|
70
|
+
entry_price: Optional[Decimal] = Field(
|
|
71
|
+
None,
|
|
72
|
+
description="Target entry price from trade signal",
|
|
73
|
+
)
|
|
74
|
+
sl_price: Optional[Decimal] = Field(
|
|
75
|
+
None,
|
|
76
|
+
description="Stop loss price from trade signal",
|
|
77
|
+
)
|
|
78
|
+
tp_price: Optional[Decimal] = Field(
|
|
79
|
+
None,
|
|
80
|
+
description="Take profit price from trade signal",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Fill tracking
|
|
84
|
+
filled_entry_qty: Decimal = Field(
|
|
85
|
+
default=Decimal("0"),
|
|
86
|
+
description="Quantity filled for entry order",
|
|
87
|
+
)
|
|
88
|
+
filled_exit_qty: Decimal = Field(
|
|
89
|
+
default=Decimal("0"),
|
|
90
|
+
description="Quantity filled for exit order",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Trading instrument (may differ from signal instrument)
|
|
94
|
+
trading_instrument_id: Optional[int] = Field(
|
|
95
|
+
None,
|
|
96
|
+
description="Trading instrument ID (FK to data.instruments)",
|
|
97
|
+
)
|
|
98
|
+
symbol: Optional[str] = Field(
|
|
99
|
+
None,
|
|
100
|
+
description="Trading symbol for the mapped instrument",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Timestamps
|
|
104
|
+
created_at: datetime = Field(..., description="Creation timestamp")
|
|
105
|
+
updated_at: datetime = Field(..., description="Last update timestamp")
|
|
106
|
+
|
|
107
|
+
# Phase helper properties
|
|
108
|
+
@property
|
|
109
|
+
def is_pending_phase(self) -> bool:
|
|
110
|
+
"""Check if engagement is in PENDING phase."""
|
|
111
|
+
return self.phase == EngagementPhase.PENDING
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def is_entering_phase(self) -> bool:
|
|
115
|
+
"""Check if engagement is in ENTERING phase."""
|
|
116
|
+
return self.phase == EngagementPhase.ENTERING
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def is_holding_phase(self) -> bool:
|
|
120
|
+
"""Check if engagement is in HOLDING phase (position open)."""
|
|
121
|
+
return self.phase == EngagementPhase.HOLDING
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def is_exiting_phase(self) -> bool:
|
|
125
|
+
"""Check if engagement is in EXITING phase."""
|
|
126
|
+
return self.phase == EngagementPhase.EXITING
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def is_closed_phase(self) -> bool:
|
|
130
|
+
"""Check if engagement is in CLOSED phase."""
|
|
131
|
+
return self.phase == EngagementPhase.CLOSED
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def is_failed_phase(self) -> bool:
|
|
135
|
+
"""Check if engagement is in FAILED phase."""
|
|
136
|
+
return self.phase == EngagementPhase.FAILED
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def is_cancelled_phase(self) -> bool:
|
|
140
|
+
"""Check if engagement is in CANCELLED phase."""
|
|
141
|
+
return self.phase == EngagementPhase.CANCELLED
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def is_exit_failed_phase(self) -> bool:
|
|
145
|
+
"""Check if engagement is in EXIT_FAILED phase."""
|
|
146
|
+
return self.phase == EngagementPhase.EXIT_FAILED
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def is_terminal(self) -> bool:
|
|
150
|
+
"""Check if engagement is in a terminal state (no more transitions)."""
|
|
151
|
+
return self.phase in (
|
|
152
|
+
EngagementPhase.CLOSED,
|
|
153
|
+
EngagementPhase.FAILED,
|
|
154
|
+
EngagementPhase.CANCELLED,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def needs_intervention(self) -> bool:
|
|
159
|
+
"""Check if engagement needs manual intervention."""
|
|
160
|
+
return self.phase == EngagementPhase.EXIT_FAILED
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Orderbook Model
|
|
3
|
+
|
|
4
|
+
Provides the OrderbookEntry model for event sourcing of order lifecycle.
|
|
5
|
+
Each event in the order lifecycle is recorded as a separate entry.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
from typing import Optional
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
from ..enums import OrderbookEventType
|
|
16
|
+
from .orders import OrderSide, OrderStatus, OrderType
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OrderbookEntry(BaseModel):
|
|
20
|
+
"""Orderbook event entry (Event Sourcing).
|
|
21
|
+
|
|
22
|
+
Records each event in the order lifecycle for audit and tracking.
|
|
23
|
+
Each order state change creates a new entry (immutable log).
|
|
24
|
+
|
|
25
|
+
Event Types:
|
|
26
|
+
- ORDER_CREATED (0): Order submitted to broker
|
|
27
|
+
- ORDER_MODIFIED (1): Order modified (price, quantity)
|
|
28
|
+
- ORDER_CANCELLED (2): Order cancelled
|
|
29
|
+
- PARTIAL_FILL (3): Order partially filled
|
|
30
|
+
- FULLY_FILLED (4): Order fully filled
|
|
31
|
+
- REJECTED (5): Order rejected by broker
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
id: UUID = Field(..., description="Entry UUID (primary key)")
|
|
35
|
+
user_id: UUID = Field(..., description="User UUID")
|
|
36
|
+
engagement_id: UUID = Field(..., description="Engagement UUID (FK)")
|
|
37
|
+
|
|
38
|
+
event_type: OrderbookEventType = Field(..., description="Event type (SMALLINT: 0-5)")
|
|
39
|
+
raw_data: dict = Field(default_factory=dict, description="Broker raw response (JSONB)")
|
|
40
|
+
|
|
41
|
+
# Order details
|
|
42
|
+
symbol: str = Field(..., description="Trading symbol")
|
|
43
|
+
side: OrderSide = Field(..., description="Order side (BUY/SELL)")
|
|
44
|
+
order_type: OrderType = Field(..., description="Order type")
|
|
45
|
+
quantity: Decimal = Field(..., description="Order quantity")
|
|
46
|
+
filled_quantity: Decimal = Field(
|
|
47
|
+
default=Decimal("0"), description="Filled quantity (for partial fills)"
|
|
48
|
+
)
|
|
49
|
+
price: Optional[Decimal] = Field(None, description="Order price (for limit orders)")
|
|
50
|
+
filled_price: Optional[Decimal] = Field(None, description="Average filled price")
|
|
51
|
+
|
|
52
|
+
# Broker tracking
|
|
53
|
+
broker_order_id: str = Field(..., description="Broker's order ID")
|
|
54
|
+
status: OrderStatus = Field(..., description="Order status")
|
|
55
|
+
|
|
56
|
+
# Timestamps
|
|
57
|
+
created_at: datetime = Field(..., description="Event timestamp")
|
|
58
|
+
updated_at: datetime = Field(..., description="Last update timestamp")
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def is_filled(self) -> bool:
|
|
62
|
+
"""Check if order is fully filled."""
|
|
63
|
+
return self.event_type == OrderbookEventType.FULLY_FILLED
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def is_partial(self) -> bool:
|
|
67
|
+
"""Check if order is partially filled."""
|
|
68
|
+
return self.event_type == OrderbookEventType.PARTIAL_FILL
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def remaining_quantity(self) -> Decimal:
|
|
72
|
+
"""Calculate remaining quantity."""
|
|
73
|
+
return self.quantity - self.filled_quantity
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Order models for unified trading interface."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
# Import OrderStrategy from existing enums module
|
|
12
|
+
from tradepose_models.enums.order_strategy import OrderStrategy
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OrderSide(str, Enum):
|
|
16
|
+
"""Order side."""
|
|
17
|
+
|
|
18
|
+
BUY = "buy"
|
|
19
|
+
SELL = "sell"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OrderType(str, Enum):
|
|
23
|
+
"""Order types."""
|
|
24
|
+
|
|
25
|
+
MARKET = "market"
|
|
26
|
+
LIMIT = "limit"
|
|
27
|
+
STOP_MARKET = "stop_market"
|
|
28
|
+
STOP_LIMIT = "stop_limit"
|
|
29
|
+
TAKE_PROFIT_MARKET = "take_profit_market"
|
|
30
|
+
TAKE_PROFIT_LIMIT = "take_profit_limit"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OrderStatus(str, Enum):
|
|
34
|
+
"""Order status."""
|
|
35
|
+
|
|
36
|
+
PENDING_NEW = "pending_new"
|
|
37
|
+
NEW = "new"
|
|
38
|
+
PARTIALLY_FILLED = "partially_filled"
|
|
39
|
+
FILLED = "filled"
|
|
40
|
+
CANCELLED = "cancelled"
|
|
41
|
+
REJECTED = "rejected"
|
|
42
|
+
EXPIRED = "expired"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TimeInForce(str, Enum):
|
|
46
|
+
"""Time in force."""
|
|
47
|
+
|
|
48
|
+
GTC = "gtc" # Good Till Cancel
|
|
49
|
+
IOC = "ioc" # Immediate or Cancel
|
|
50
|
+
FOK = "fok" # Fill or Kill
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class OrderSubmitRequest(BaseModel):
|
|
54
|
+
"""Unified order submission request."""
|
|
55
|
+
|
|
56
|
+
user_id: UUID
|
|
57
|
+
account_id: UUID
|
|
58
|
+
symbol: str = Field(..., description="Unified symbol (e.g., BTC-USDT)")
|
|
59
|
+
side: OrderSide
|
|
60
|
+
order_type: OrderType
|
|
61
|
+
quantity: Decimal
|
|
62
|
+
price: Optional[Decimal] = None
|
|
63
|
+
stop_price: Optional[Decimal] = None
|
|
64
|
+
time_in_force: TimeInForce = TimeInForce.GTC
|
|
65
|
+
client_order_id: Optional[str] = None
|
|
66
|
+
reduce_only: bool = False
|
|
67
|
+
|
|
68
|
+
# Trade/Portfolio context
|
|
69
|
+
snapshot_price: Optional[Decimal] = Field(None, description="Market price at submission")
|
|
70
|
+
trade_id: Optional[str] = Field(None, description="Backtest trade ID")
|
|
71
|
+
portfolio_id: Optional[str] = Field(None, description="Portfolio ID")
|
|
72
|
+
order_strategy: Optional[OrderStrategy] = Field(None, description="Execution strategy")
|
|
73
|
+
|
|
74
|
+
extra_params: dict = Field(default_factory=dict)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Order(BaseModel):
|
|
78
|
+
"""Unified order model."""
|
|
79
|
+
|
|
80
|
+
order_id: str
|
|
81
|
+
client_order_id: Optional[str]
|
|
82
|
+
user_id: UUID
|
|
83
|
+
account_id: UUID
|
|
84
|
+
broker: str
|
|
85
|
+
symbol: str = Field(..., description="Unified symbol")
|
|
86
|
+
broker_symbol: str = Field(..., description="Broker-specific symbol")
|
|
87
|
+
side: OrderSide
|
|
88
|
+
order_type: OrderType
|
|
89
|
+
quantity: Decimal
|
|
90
|
+
price: Optional[Decimal]
|
|
91
|
+
stop_price: Optional[Decimal]
|
|
92
|
+
filled_quantity: Decimal
|
|
93
|
+
remaining_quantity: Decimal
|
|
94
|
+
average_fill_price: Optional[Decimal]
|
|
95
|
+
status: OrderStatus
|
|
96
|
+
time_in_force: TimeInForce
|
|
97
|
+
created_at: datetime
|
|
98
|
+
updated_at: datetime
|
|
99
|
+
commission: Optional[Decimal] = None
|
|
100
|
+
commission_asset: Optional[str] = None
|
|
101
|
+
|
|
102
|
+
# Trade/Portfolio tracking
|
|
103
|
+
trade_id: Optional[str] = None
|
|
104
|
+
portfolio_id: Optional[str] = None
|
|
105
|
+
order_strategy: Optional[OrderStrategy] = None
|
|
106
|
+
|
|
107
|
+
raw_data: Optional[dict] = None # Broker's raw response
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ExecutionReport(BaseModel):
|
|
111
|
+
"""Unified execution report (fill notification)."""
|
|
112
|
+
|
|
113
|
+
execution_id: str
|
|
114
|
+
order_id: str
|
|
115
|
+
client_order_id: Optional[str]
|
|
116
|
+
user_id: UUID
|
|
117
|
+
account_id: UUID
|
|
118
|
+
broker: str
|
|
119
|
+
symbol: str
|
|
120
|
+
broker_symbol: str
|
|
121
|
+
side: OrderSide
|
|
122
|
+
last_filled_quantity: Decimal
|
|
123
|
+
last_filled_price: Optional[Decimal]
|
|
124
|
+
cumulative_filled_quantity: Decimal
|
|
125
|
+
remaining_quantity: Decimal
|
|
126
|
+
order_status: OrderStatus
|
|
127
|
+
commission: Decimal
|
|
128
|
+
commission_asset: str
|
|
129
|
+
transact_time: datetime
|
|
130
|
+
received_at: datetime
|
|
131
|
+
|
|
132
|
+
# Trade/Portfolio tracking
|
|
133
|
+
trade_id: Optional[str] = None
|
|
134
|
+
portfolio_id: Optional[str] = None
|
|
135
|
+
order_strategy: Optional[OrderStrategy] = None
|
|
136
|
+
|
|
137
|
+
raw_data: Optional[dict] = None
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Position models."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PositionSide(str, Enum):
|
|
13
|
+
"""Position side."""
|
|
14
|
+
|
|
15
|
+
LONG = "long"
|
|
16
|
+
SHORT = "short"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Position(BaseModel):
|
|
20
|
+
"""Unified position model."""
|
|
21
|
+
|
|
22
|
+
# Core fields (required)
|
|
23
|
+
symbol: str # Unified symbol
|
|
24
|
+
broker_symbol: str # Broker-specific symbol
|
|
25
|
+
side: PositionSide
|
|
26
|
+
quantity: Decimal
|
|
27
|
+
entry_price: Decimal # Average cost
|
|
28
|
+
current_price: Decimal
|
|
29
|
+
unrealized_pnl: Decimal
|
|
30
|
+
|
|
31
|
+
# With defaults (was required, now has sensible defaults)
|
|
32
|
+
realized_pnl: Decimal = Decimal(0)
|
|
33
|
+
created_at: datetime = Field(default_factory=datetime.now)
|
|
34
|
+
updated_at: datetime = Field(default_factory=datetime.now)
|
|
35
|
+
|
|
36
|
+
# Existing optional fields
|
|
37
|
+
margin_used: Optional[Decimal] = None
|
|
38
|
+
leverage: Optional[Decimal] = None
|
|
39
|
+
raw_data: Optional[dict] = None
|
|
40
|
+
|
|
41
|
+
# Broker-specific identifiers (optional)
|
|
42
|
+
position_id: Optional[str] = None
|
|
43
|
+
user_id: Optional[UUID] = None
|
|
44
|
+
account_id: Optional[UUID] = None
|
|
45
|
+
broker: Optional[str] = None
|
|
46
|
+
opened_at: Optional[datetime] = None
|
|
47
|
+
|
|
48
|
+
# Risk management (optional)
|
|
49
|
+
stop_loss: Optional[Decimal] = None
|
|
50
|
+
take_profit: Optional[Decimal] = None
|
|
51
|
+
|
|
52
|
+
# MT5-specific (optional)
|
|
53
|
+
swap: Optional[Decimal] = None
|
|
54
|
+
magic: Optional[int] = None
|
|
55
|
+
comment: Optional[str] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ClosedPosition(BaseModel):
|
|
59
|
+
"""Closed position (historical)."""
|
|
60
|
+
|
|
61
|
+
# Required fields
|
|
62
|
+
symbol: str
|
|
63
|
+
side: PositionSide
|
|
64
|
+
quantity: Decimal
|
|
65
|
+
entry_price: Decimal
|
|
66
|
+
exit_price: Decimal
|
|
67
|
+
realized_pnl: Decimal
|
|
68
|
+
commission: Decimal
|
|
69
|
+
opened_at: datetime
|
|
70
|
+
closed_at: datetime
|
|
71
|
+
|
|
72
|
+
# Broker-specific (optional)
|
|
73
|
+
position_id: Optional[str] = None
|
|
74
|
+
user_id: Optional[UUID] = None
|
|
75
|
+
account_id: Optional[UUID] = None
|
|
76
|
+
broker: Optional[str] = None
|
|
77
|
+
broker_symbol: Optional[str] = None
|
|
78
|
+
swap: Optional[Decimal] = None
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Trader command models for Redis Stream communication (Issue #307, #320).
|
|
2
|
+
|
|
3
|
+
Pydantic models for trader commands published to Redis Streams:
|
|
4
|
+
- ExecuteOrderCommand: Open a new position
|
|
5
|
+
- CancelOrderCommand: Cancel an existing order
|
|
6
|
+
- ModifyOrderCommand: Modify SL/TP on existing order
|
|
7
|
+
- SyncBrokerStatusCommand: Request broker status sync
|
|
8
|
+
|
|
9
|
+
Stream pattern: trader:commands:{user_id}:{node_seq}:{slot_idx}
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from decimal import Decimal
|
|
14
|
+
from typing import Literal, Optional
|
|
15
|
+
from uuid import UUID
|
|
16
|
+
|
|
17
|
+
from pydantic import BaseModel, Field
|
|
18
|
+
|
|
19
|
+
from tradepose_models.enums import TradeDirection
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BaseTraderCommand(BaseModel):
|
|
23
|
+
"""Base class for all trader commands."""
|
|
24
|
+
|
|
25
|
+
timestamp: datetime = Field(
|
|
26
|
+
...,
|
|
27
|
+
description="Command creation timestamp (UTC)",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ExecuteOrderCommand(BaseTraderCommand):
|
|
32
|
+
"""Command to execute a new order.
|
|
33
|
+
|
|
34
|
+
Published when an engagement is created and ready for execution.
|
|
35
|
+
Contains all information needed to place entry order with SL/TP.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
command_type: Literal["execute_order"] = Field(
|
|
39
|
+
default="execute_order",
|
|
40
|
+
description="Command type identifier",
|
|
41
|
+
)
|
|
42
|
+
engagement_id: UUID = Field(
|
|
43
|
+
...,
|
|
44
|
+
description="Engagement UUID for tracking",
|
|
45
|
+
)
|
|
46
|
+
account_id: UUID = Field(
|
|
47
|
+
...,
|
|
48
|
+
description="Trading account UUID",
|
|
49
|
+
)
|
|
50
|
+
symbol: str = Field(
|
|
51
|
+
...,
|
|
52
|
+
description="Trading symbol (e.g., XAUUSD, MNQ)",
|
|
53
|
+
)
|
|
54
|
+
direction: TradeDirection = Field(
|
|
55
|
+
...,
|
|
56
|
+
description="Trade direction: Long or Short",
|
|
57
|
+
)
|
|
58
|
+
quantity: Decimal = Field(
|
|
59
|
+
...,
|
|
60
|
+
gt=0,
|
|
61
|
+
description="Order quantity (lots)",
|
|
62
|
+
)
|
|
63
|
+
entry_price: Decimal = Field(
|
|
64
|
+
...,
|
|
65
|
+
gt=0,
|
|
66
|
+
description="Target entry price",
|
|
67
|
+
)
|
|
68
|
+
sl_price: Optional[Decimal] = Field(
|
|
69
|
+
None,
|
|
70
|
+
description="Stop loss price (optional)",
|
|
71
|
+
)
|
|
72
|
+
tp_price: Optional[Decimal] = Field(
|
|
73
|
+
None,
|
|
74
|
+
description="Take profit price (optional)",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CancelOrderCommand(BaseTraderCommand):
|
|
79
|
+
"""Command to cancel an existing order."""
|
|
80
|
+
|
|
81
|
+
command_type: Literal["cancel_order"] = Field(
|
|
82
|
+
default="cancel_order",
|
|
83
|
+
description="Command type identifier",
|
|
84
|
+
)
|
|
85
|
+
engagement_id: UUID = Field(
|
|
86
|
+
...,
|
|
87
|
+
description="Engagement UUID for tracking",
|
|
88
|
+
)
|
|
89
|
+
account_id: UUID = Field(
|
|
90
|
+
...,
|
|
91
|
+
description="Trading account UUID",
|
|
92
|
+
)
|
|
93
|
+
broker_order_id: Optional[str] = Field(
|
|
94
|
+
None,
|
|
95
|
+
description="Broker's order ID to cancel (if known)",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ModifyOrderCommand(BaseTraderCommand):
|
|
100
|
+
"""Command to modify an existing order's SL/TP."""
|
|
101
|
+
|
|
102
|
+
command_type: Literal["modify_order"] = Field(
|
|
103
|
+
default="modify_order",
|
|
104
|
+
description="Command type identifier",
|
|
105
|
+
)
|
|
106
|
+
engagement_id: UUID = Field(
|
|
107
|
+
...,
|
|
108
|
+
description="Engagement UUID for tracking",
|
|
109
|
+
)
|
|
110
|
+
account_id: UUID = Field(
|
|
111
|
+
...,
|
|
112
|
+
description="Trading account UUID",
|
|
113
|
+
)
|
|
114
|
+
broker_order_id: str = Field(
|
|
115
|
+
...,
|
|
116
|
+
description="Broker's order ID to modify",
|
|
117
|
+
)
|
|
118
|
+
new_sl_price: Optional[Decimal] = Field(
|
|
119
|
+
None,
|
|
120
|
+
description="New stop loss price",
|
|
121
|
+
)
|
|
122
|
+
new_tp_price: Optional[Decimal] = Field(
|
|
123
|
+
None,
|
|
124
|
+
description="New take profit price",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class SyncBrokerStatusCommand(BaseTraderCommand):
|
|
129
|
+
"""Command to request broker status synchronization."""
|
|
130
|
+
|
|
131
|
+
command_type: Literal["sync_broker_status"] = Field(
|
|
132
|
+
default="sync_broker_status",
|
|
133
|
+
description="Command type identifier",
|
|
134
|
+
)
|
|
135
|
+
account_id: UUID = Field(
|
|
136
|
+
...,
|
|
137
|
+
description="Trading account UUID to sync",
|
|
138
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Trade execution record models."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from typing import Literal, Optional
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from tradepose_models.trading.orders import OrderSide
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TradeExecution(BaseModel):
|
|
13
|
+
"""Trade execution record (actual fill)."""
|
|
14
|
+
|
|
15
|
+
trade_id: str
|
|
16
|
+
order_id: str
|
|
17
|
+
client_order_id: Optional[str]
|
|
18
|
+
symbol: str
|
|
19
|
+
broker_symbol: str
|
|
20
|
+
side: OrderSide
|
|
21
|
+
price: Decimal
|
|
22
|
+
quantity: Decimal
|
|
23
|
+
commission: Decimal
|
|
24
|
+
commission_asset: str
|
|
25
|
+
commission_type: Literal["maker", "taker", "fixed"]
|
|
26
|
+
timestamp: datetime
|
|
27
|
+
raw_data: Optional[dict] = None
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Common type definitions (通用類型定義).
|
|
2
|
+
|
|
3
|
+
This module contains type aliases and custom types used across the platform
|
|
4
|
+
to improve code clarity and type safety.
|
|
5
|
+
|
|
6
|
+
這個模組包含平台上使用的類型別名和自定義類型,以提高代碼清晰度和類型安全性。
|
|
7
|
+
|
|
8
|
+
Example usage:
|
|
9
|
+
from tradepose_models.types import UserId, APIKeyHash, Timestamp
|
|
10
|
+
|
|
11
|
+
def get_user(user_id: UserId) -> dict:
|
|
12
|
+
# Type hints make the code more readable
|
|
13
|
+
return {"id": user_id}
|
|
14
|
+
|
|
15
|
+
def verify_key(key_hash: APIKeyHash) -> bool:
|
|
16
|
+
# Custom types document intent
|
|
17
|
+
return check_hash(key_hash)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Placeholder for shared type definitions
|
|
22
|
+
# Types will be added here as needed
|
|
23
|
+
# 共用類型定義將根據需要添加到這裡
|
|
24
|
+
|
|
25
|
+
# Example structure:
|
|
26
|
+
# UserId = NewType('UserId', str)
|
|
27
|
+
# """User identifier type (用戶標識符類型)."""
|
|
28
|
+
#
|
|
29
|
+
# APIKeyHash = NewType('APIKeyHash', str)
|
|
30
|
+
# """Hashed API key type (API 密鑰哈希類型)."""
|
|
31
|
+
#
|
|
32
|
+
# Timestamp = NewType('Timestamp', datetime)
|
|
33
|
+
# """Timestamp type for consistency (時間戳類型以保持一致性)."""
|
|
34
|
+
|
|
35
|
+
__all__ = []
|