diff --git a/CMakeLists.txt b/CMakeLists.txt index c9e2b18..f59e6c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -757,6 +757,25 @@ if(OPTIONX_BUILD_EXAMPLES) endif() target_link_libraries(telegram_signal_bridge_smoke PRIVATE ${EXAMPLE_LIBS} optionx_cpp) + add_executable(telegram_archive_parser_smoke examples/telegram_archive_parser_smoke.cpp) + target_compile_features(telegram_archive_parser_smoke PRIVATE cxx_std_17) + target_include_directories(telegram_archive_parser_smoke PRIVATE + ${EXAMPLE_INCLUDE_DIRS} + ${EXAMPLE_DEPS_INCLUDE_DIRS} + ) + target_link_directories(telegram_archive_parser_smoke PRIVATE ${EXAMPLE_LIBRARY_DIRS}) + target_compile_definitions( + telegram_archive_parser_smoke PRIVATE + ${EXAMPLE_DEFINES} + LOGIT_BASE_PATH="${LOGIT_BASE_PATH_FWD}" + ) + if(MINGW) + target_compile_options(telegram_archive_parser_smoke PRIVATE -Wa,-mbig-obj) + elseif(MSVC) + target_compile_options(telegram_archive_parser_smoke PRIVATE /bigobj) + endif() + target_link_libraries(telegram_archive_parser_smoke PRIVATE ${EXAMPLE_LIBS} optionx_cpp) + add_executable(protocol_v1_bridge_smoke examples/protocol_v1_bridge_smoke.cpp) target_compile_features(protocol_v1_bridge_smoke PRIVATE cxx_std_17) diff --git a/examples/README.md b/examples/README.md index d4cd657..b0650af 100644 --- a/examples/README.md +++ b/examples/README.md @@ -59,6 +59,10 @@ Currently maintained examples: bridge lifecycle with a deterministic in-memory source. The production source boundary can be backed by `tg-client-stdio`; this example needs no Telegram credentials. +- `telegram_archive_parser_smoke.cpp` replays exported Telegram raw-message + records through the parser and keeps executable signals, outcomes and + diagnostics separate. It uses deterministic fixtures and needs no Telegram + credentials. - `metatrader_file_bridge_smoke.cpp` runs the C++ side of the MetaTrader Common\Files bridge against a temporary command/event layout. - `metatrader_file_command_writer_smoke.cpp` demonstrates the C++ command-writer diff --git a/examples/telegram_archive_parser_smoke.cpp b/examples/telegram_archive_parser_smoke.cpp new file mode 100644 index 0000000..4179164 --- /dev/null +++ b/examples/telegram_archive_parser_smoke.cpp @@ -0,0 +1,81 @@ +/// \file telegram_archive_parser_smoke.cpp +/// \brief Demonstrates replaying exported Telegram messages through the parser. + +#include + +#include +#include +#include +#include +#include + +namespace { + +optionx::bridges::telegram::TelegramRawMessage make_message( + std::int64_t message_id, + std::string text, + std::int64_t reply_to_message_id = 0); + +std::vector demo_archive(); + +void print_parsed( + const optionx::bridges::telegram::TelegramParsedMessage& parsed); + +} // namespace + +int main() { + // In production these records arrive one at a time from + // tg-client-stdio::WorkerClient::stream_messages(). Keeping the replay + // loop source-independent makes parser tests and backtests deterministic. + optionx::bridges::telegram::TelegramSignalParser parser; + for (const auto& raw : demo_archive()) { + print_parsed(parser.parse(raw)); + } + return 0; +} + +namespace { + +optionx::bridges::telegram::TelegramRawMessage make_message( + const std::int64_t message_id, + std::string text, + const std::int64_t reply_to_message_id) { + optionx::bridges::telegram::TelegramRawMessage message; + message.chat_id = "-1001234567890"; + message.chat_title = "Demo archive"; + message.message_id = message_id; + message.date_ms = 1800000000000 + message_id * 1000; + message.reply_to_message_id = reply_to_message_id; + message.text = std::move(text); + return message; +} + +std::vector demo_archive() { + return { + make_message(100, "EURUSD BUY 5m"), + make_message(101, "EURUSD WIN", 100), + make_message(102, "USDJPY BUY 2 weeks"), + }; +} + +void print_parsed( + const optionx::bridges::telegram::TelegramParsedMessage& parsed) { + std::cout << "message=" << parsed.raw.message_identity() + << " signals=" << parsed.signals.size() + << " outcomes=" << parsed.outcomes.size() + << " diagnostics=" << parsed.diagnostics.size() << '\n'; + for (const auto& signal : parsed.signals) { + std::cout << " signal=" << signal.symbol + << " direction=" << optionx::to_str(signal.order_type) + << " duration=" << signal.duration << '\n'; + } + for (const auto& outcome : parsed.outcomes) { + std::cout << " outcome=" << outcome.symbol + << " result=" << static_cast(outcome.result) << '\n'; + } + for (const auto& diagnostic : parsed.diagnostics) { + std::cout << " diagnostic=" << diagnostic.code << '\n'; + } +} + +} // namespace diff --git a/guides/telegram-bridge-design.md b/guides/telegram-bridge-design.md index b040b8e..bed09d4 100644 --- a/guides/telegram-bridge-design.md +++ b/guides/telegram-bridge-design.md @@ -56,9 +56,10 @@ stdio boundary. The current OptionX bridge does not own a Telethon process. It consumes the `TelegramMessageSource` interface, so fake sources can exercise parsing and -lifecycle without credentials. The concrete source adapter will bind that -interface to `tg-client-stdio::WorkerClient` after the worker repository's -supervisor and typed archive API are merged and pinned by OptionX. +lifecycle without credentials. `TelegramWorkerMessageSource` now binds that +interface to a WorkerClient-shaped process adapter; the parent repository will +pin the concrete `tg-client-stdio::WorkerClient` only after its stacked worker +PRs are merged. ## Stdio Protocol Envelope @@ -332,13 +333,17 @@ Completed without an authorized Telegram session: source-independent `TelegramSignalBridge`. 4. Fake-source unit coverage and a runnable no-credentials bridge example. +Current no-credentials examples include a live fake-source bridge smoke and a +deterministic archive/parser replay. The latter uses the same raw-message shape +that `messages.export` streams, while keeping parser outcomes separate from +executable signals. + Next steps: 1. Merge and pin the worker repository's supervisor/archive PRs. 2. Pin the merged worker repository in an OptionX consumer and run the adapter against the mock worker process. -3. Add a historical archive/parser fixture example. -4. Perform the first real authorization, proxy and live-channel check with an +3. Perform the first real authorization, proxy and live-channel check with an operator-provided Telegram session. OCR/vision remains a separate optional provider and should not block the text