Skip to content

Commit b9f5ef1

Browse files
authored
Use shared execution outcome helpers (#21)
* Use shared execution outcome helpers * Update UsEquityStrategies dependency
1 parent b915a49 commit b9f5ef1

4 files changed

Lines changed: 34 additions & 54 deletions

File tree

application/rebalance_service.py

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,22 @@
3535
)
3636
from decision_mapper import map_strategy_decision_to_plan
3737
from notifications.telegram import build_sender, build_translator, render_cycle_summary
38+
from quant_platform_kit.common.execution_outcomes import (
39+
filter_execution_blocking_skips,
40+
is_terminal_funding_block,
41+
resolve_strategy_run_stage,
42+
)
3843
from quant_platform_kit.common.runtime_inputs import (
3944
build_semiconductor_rotation_indicators_from_history,
4045
required_semiconductor_rotation_history_lookback,
4146
)
47+
from quant_platform_kit.notifications.events import NotificationPublisher, RenderedNotification
4248
from quant_platform_kit.strategy_contracts import build_strategy_evaluation_inputs
4349
from runtime_config_support import PlatformRuntimeSettings, load_platform_runtime_settings
4450
from strategy_runtime import load_strategy_runtime
4551

4652
LIMIT_SELL_DISCOUNT = 0.995
4753
LIMIT_BUY_PREMIUM = 1.005
48-
EXECUTION_BLOCKING_SKIP_REASONS = frozenset(
49-
{
50-
"buy_quantity_zero",
51-
"insufficient_cash_for_whole_share",
52-
"quote_unavailable",
53-
"sell_quantity_zero",
54-
}
55-
)
56-
TERMINAL_FUNDING_BLOCK_SKIP_REASONS = frozenset({"insufficient_cash_for_whole_share"})
5754

5855

5956
def _utcnow() -> datetime:
@@ -64,41 +61,6 @@ def get_project_id() -> str | None:
6461
return os.getenv("GOOGLE_CLOUD_PROJECT")
6562

6663

67-
def _execution_blocking_skips(skipped_orders: list[dict[str, Any]]) -> list[dict[str, Any]]:
68-
return [
69-
dict(item)
70-
for item in skipped_orders
71-
if str(item.get("reason") or "") in EXECUTION_BLOCKING_SKIP_REASONS
72-
]
73-
74-
75-
def _is_terminal_funding_block(blocking_skips: list[dict[str, Any]]) -> bool:
76-
if not blocking_skips:
77-
return False
78-
return all(
79-
str(item.get("reason") or "") in TERMINAL_FUNDING_BLOCK_SKIP_REASONS
80-
for item in blocking_skips
81-
)
82-
83-
84-
def _resolve_strategy_run_stage(
85-
*,
86-
dry_run_only: bool,
87-
execution_blocked: bool,
88-
terminal_funding_block: bool,
89-
action_done: bool,
90-
) -> str:
91-
if dry_run_only:
92-
return "DRY_RUN_COMPLETED"
93-
if terminal_funding_block and not action_done:
94-
return "FUNDING_BLOCKED"
95-
if execution_blocked and action_done:
96-
return "PARTIAL_SUBMITTED"
97-
if execution_blocked:
98-
return "EXECUTION_BLOCKED"
99-
return "SUBMITTED" if action_done else "NO_ACTION"
100-
101-
10264
def _series_from_price_history(market_data_port, symbol: str) -> pd.Series:
10365
series = market_data_port.get_price_series(symbol)
10466
index = pd.DatetimeIndex([pd.Timestamp(point.as_of) for point in series.points])
@@ -179,13 +141,24 @@ def _publish_cycle_notification(
179141
*,
180142
settings: PlatformRuntimeSettings,
181143
notification_sender: Callable[[str], None] | None = None,
144+
log_message: Callable[[str], None] = print,
182145
) -> bool:
183146
sender = notification_sender
184147
if sender is None:
185148
if not settings.tg_token or not settings.tg_chat_id:
186149
return False
187150
sender = build_sender(settings.tg_token, settings.tg_chat_id)
188-
sender(render_cycle_summary(result, lang=settings.notify_lang))
151+
message = render_cycle_summary(result, lang=settings.notify_lang)
152+
def publish_log(text: str) -> None:
153+
try:
154+
log_message(text, flush=True)
155+
except TypeError:
156+
log_message(text)
157+
158+
NotificationPublisher(
159+
log_message=publish_log,
160+
send_message=sender,
161+
).publish(RenderedNotification(detailed_text=message, compact_text=message))
189162
return True
190163

191164

@@ -330,11 +303,11 @@ def run_strategy_cycle(
330303
)
331304
submitted_orders = list(execution_result.submitted_orders)
332305
skipped_orders = list(execution_result.skipped_orders)
333-
blocking_skips = _execution_blocking_skips(skipped_orders)
306+
blocking_skips = filter_execution_blocking_skips(skipped_orders)
334307
execution_blocked = bool(blocking_skips)
335-
funding_blocked = _is_terminal_funding_block(blocking_skips)
308+
funding_blocked = is_terminal_funding_block(blocking_skips)
336309
terminal_funding_block = funding_blocked and not execution_result.action_done
337-
strategy_run_stage = _resolve_strategy_run_stage(
310+
strategy_run_stage = resolve_strategy_run_stage(
338311
dry_run_only=settings.dry_run_only,
339312
execution_blocked=execution_blocked,
340313
terminal_funding_block=terminal_funding_block,

application/strategy_run_persistence.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
from typing import Any
1010

1111
from application.state_persistence import GcsStateStore
12+
from quant_platform_kit.common.execution_outcomes import (
13+
DEFAULT_TERMINAL_STRATEGY_RUN_STAGES,
14+
is_terminal_strategy_run_stage,
15+
)
1216

13-
LIVE_TERMINAL_STAGES = frozenset({"SUBMITTED", "FUNDING_BLOCKED", "RECONCILED", "COMPLETED"})
17+
LIVE_TERMINAL_STAGES = DEFAULT_TERMINAL_STRATEGY_RUN_STAGES
1418

1519

1620
def utcnow() -> datetime:
@@ -118,7 +122,10 @@ def is_duplicate_live_run(existing_state: Mapping[str, Any] | None) -> bool:
118122
return False
119123
if bool(existing_state.get("dry_run_only")):
120124
return False
121-
return str(existing_state.get("stage") or "").strip().upper() in LIVE_TERMINAL_STAGES
125+
return is_terminal_strategy_run_stage(
126+
existing_state.get("stage"),
127+
terminal_stages=LIVE_TERMINAL_STAGES,
128+
)
122129

123130

124131
def build_strategy_run_state(

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ authors = [
1414
]
1515
dependencies = [
1616
"firstrade==0.0.38",
17-
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@663e80be60b0da80e81513b711c579d221a2111d",
18-
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@4d5cd0f5dc389edebc648028202fd116934ca325",
17+
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@9c4ea7878a08fb2f518c74c99bda68d8ef8fd0bb",
18+
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@ce7887482eeab7f519484610ee8b20cb7bc886a0",
1919
"requests",
2020
]
2121

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
flask
22
gunicorn
33
firstrade==0.0.38
4-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@663e80be60b0da80e81513b711c579d221a2111d
5-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@4d5cd0f5dc389edebc648028202fd116934ca325
4+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@9c4ea7878a08fb2f518c74c99bda68d8ef8fd0bb
5+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@ce7887482eeab7f519484610ee8b20cb7bc886a0
66
requests
77
pytest

0 commit comments

Comments
 (0)