feat dev6

This commit is contained in:
2026-09-01 14:28:49 +08:00
parent dedbf63a92
commit 556e21d624
27 changed files with 334 additions and 144 deletions

View File

@@ -0,0 +1,33 @@
from types import SimpleNamespace
import unittest
from unittest.mock import patch
from libs.signal import SignalResult, fetch_signal, init_signals
class SignalTests(unittest.TestCase):
def test_fetch_failure_returns_empty_result(self):
with patch("libs.signal.get_json", side_effect=OSError("offline")):
self.assertEqual(fetch_signal("http://example", "/signals"), SignalResult())
def test_init_signals_continues_after_fetch_failure(self):
config = SimpleNamespace(
api_host="http://example",
signals={
"failed": SimpleNamespace(url="/failed"),
"working": SimpleNamespace(url="/working"),
},
)
responses = [
SignalResult(),
SignalResult(data={"000001.SZ": SimpleNamespace(signal_key="")}),
]
with patch("libs.signal.fetch_signal", side_effect=responses):
result = init_signals(config, ["failed", "working"])
self.assertEqual(len(result), 1)
self.assertEqual(result[0].signal_key, "working")
if __name__ == "__main__":
unittest.main()

View File

@@ -8,7 +8,7 @@ from types import SimpleNamespace
from unittest.mock import patch
from libs.grid_take_profit import GridState, GridTrailingTracker
from sdk import Assets, OrderItem, PositionItem, Tick
from sdk import APIError, Assets, OrderItem, PositionItem, Tick
from strategy.trend.order import OrderBook, PlaceOrderRequest
from strategy.trend.positions import LOSS_TIERS, handle_loss, manage_positions
from strategy.trend.boot import RunOnce
@@ -36,6 +36,11 @@ class FakeOrderClient:
self.canceled.append(order_id)
class FailedOrderClient:
def passorder_latest_tagged(self, *_args):
raise APIError(502, "QMT did not return a valid order reference")
class TrendTests(unittest.TestCase):
def test_grid_states_and_account_isolation(self):
tracker = GridTrailingTracker(1)
@@ -53,6 +58,17 @@ class TrendTests(unittest.TestCase):
self.assertTrue(book.place(request))
self.assertTrue(book.busy("000001.SZ", "BUY"))
def test_order_api_error_returns_false_with_traceback(self):
book = OrderBook()
request = PlaceOrderRequest(FailedOrderClient(), 23, "000001.SZ", 100, "local", "morning")
with self.assertLogs(level="ERROR") as captured:
self.assertFalse(book.place(request))
output = "\n".join(captured.output)
self.assertIn("HTTP状态=502", output)
self.assertIn("Traceback", output)
def test_refresh_tracks_active_and_completed_and_cancels_expired(self):
old = datetime.now() - timedelta(seconds=20)
orders = [
@@ -90,6 +106,40 @@ class TrendTests(unittest.TestCase):
)
manage_positions(runtime, {"000001.SZ": Tick(last_price=10.1)}, [position], True, 5000)
def test_position_log_contains_code_name_profit_and_loss_actions(self):
with TemporaryDirectory() as directory:
state = State.for_strategy(directory, "trend", "A")
position = PositionItem(
stock_code="000001.SZ", stock_name="平安银行", volume=100,
can_use_volume=100, open_price=10, market_value=1000,
)
state.sync_positions([position])
runtime = SimpleNamespace(
client=FakeClient(), state=state, orders=OrderBook(),
add_watch=SimpleNamespace(triggered=lambda *_args: False),
profit_tracker=GridTrailingTracker(1),
account_cfg=SimpleNamespace(
account_id="A", excluded_codes=[], grid_step_pct=1,
enable_loss_add_position=False, buy_value=5000,
strategy="trend",
),
)
with self.assertLogs(level="INFO") as captured:
manage_positions(
runtime,
{"000001.SZ": Tick(last_price=10.1)},
[position],
True,
5000,
)
output = "\n".join(captured.output)
self.assertIn("代码=000001.SZ", output)
self.assertIn("名称=平安银行", output)
self.assertIn("止盈=未触发", output)
self.assertIn("补仓=未启用", output)
def test_loss_tier_boundary_does_not_overflow(self):
self.assertEqual(len(LOSS_TIERS), 2)
with TemporaryDirectory() as directory: