Official .NET SDK for LSports Trade360 Services
The Trade360 .NET SDK is a comprehensive client library that simplifies integration with LSports Trade360 ecosystem. It provides developers with a robust, type-safe interface to consume real-time sports data feeds, manage subscriptions, and access metadata through RESTful APIs. The SDK is designed to handle high-throughput scenarios with automatic recovery, connection management, and seamless distribution control.
Primary Responsibilities:
- Real-time sports data consumption via RabbitMQ message broker
- HTTP-based snapshot recovery for data consistency
- Customer subscription and metadata management
- Automatic connection recovery and health monitoring
- Message routing and deserialization with strong typing
Role in STM Ecosystem:
This SDK serves as the primary integration point for Trade360 customers, enabling them to receive live sports data updates (fixtures, livescore, markets, settlements) and manage their data subscriptions programmatically.
| Technology | Version | Purpose |
|---|---|---|
| .NET Standard | 2.1 | Cross-platform compatibility |
| C# | 8.0+ | Primary programming language |
| RabbitMQ.Client | 6.8.1 | Message broker connectivity |
| System.Text.Json | 9.0.0 | JSON serialization/deserialization |
| AutoMapper | 12.0.1 | Object-to-object mapping |
| Polly | 8.4.2 | Resilience and transient fault handling |
| xUnit | 2.9.2 | Unit testing framework |
| FluentAssertions | 6.12.1 | Test assertions |
- Message Broker: RabbitMQ for real-time data streaming
- HTTP Protocol: RESTful APIs for snapshot and metadata operations
- Dependency Injection: Microsoft.Extensions.DependencyInjection
- Configuration: Microsoft.Extensions.Configuration
- Logging: Microsoft.Extensions.Logging
| Platform | Supported Versions |
|---|---|
| .NET Core | 3.0, 3.1, 5.0, 6.0, 7.0, 8.0+ |
| .NET | 5.0, 6.0, 7.0, 8.0+ |
| Mono | 6.4+ |
| Xamarin.iOS | 10.14+ |
| Xamarin.Mac | 3.8+ |
| Xamarin.Android | 8.0+ |
| Unity | 2018.1+ |
The Trade360 SDK operates on a pub/sub messaging model for real-time data distribution and a request/response model for API operations.
- Feed Consumption: Clients connect to RabbitMQ queues to receive real-time updates
- Message Routing: Messages are automatically deserialized and routed to appropriate handlers based on message type
- Recovery: On connection loss, the SDK automatically recovers and uses Snapshot API to fill data gaps
- Subscription Management: Customers API controls which fixtures/leagues are subscribed
- Metadata Access: Static reference data (sports, leagues, markets) is available via HTTP endpoints
- Fixture Updates (
Type 1): Match metadata, schedules, participants - Livescore Updates (
Type 2): Real-time scores, incidents, statistics - Market Updates (
Type 3): Betting markets, odds, providers - Settlement Updates (
Type 35): Market results and settlements - Outright Updates (
Type 37-43): Tournament and outright market data - Heartbeat (
Type 32): Connection health monitoring
The SDK follows a layered architecture with clear separation of concerns:
graph TB
subgraph "Client Application"
App[Your Application]
Handlers[Message Handlers]
end
subgraph "SDK Layer"
Feed[Feed SDK]
Snapshot[Snapshot API]
Customers[Customers API]
Common[Common Entities]
end
subgraph "Transport Layer"
RMQ[RabbitMQ Client]
HTTP[HTTP Client]
end
subgraph "Trade360 Services"
Broker[RabbitMQ Broker]
SnapshotSvc[Snapshot Service]
CustomersSvc[Customers Service]
end
App --> Handlers
Handlers --> Feed
App --> Snapshot
App --> Customers
Feed --> Common
Snapshot --> Common
Customers --> Common
Feed --> RMQ
Snapshot --> HTTP
Customers --> HTTP
RMQ --> Broker
HTTP --> SnapshotSvc
HTTP --> CustomersSvc
trade360-dotnet-sdk/
βββ src/
β βββ Trade360SDK.Common.Entities/ # Shared entities and message types
β βββ Trade360SDK.Feed/ # Feed abstraction layer
β βββ Trade360SDK.Feed.RabbitMQ/ # RabbitMQ implementation
β βββ Trade360SDK.SnapshotApi/ # Snapshot HTTP client
β βββ Trade360SDK.CustomersApi/ # Customers HTTP client
β βββ Trade360SDK.Microsoft.DependencyInjection/ # DI extensions
βββ sdk.samples/ # Example implementations
β βββ Trade360SDK.Feed.Example/
β βββ Trade360SDK.SnapshotApi.Example/
β βββ Trade360SDK.CustomersApi.Example/
βββ test/ # Unit and integration tests
βββ Trade360SDK.Common.Entities.Tests/
βββ Trade360SDK.Feed.Tests/
βββ Trade360SDK.Feed.RabbitMQ.Tests/
βββ Trade360SDK.SnapshotApi.Tests/
βββ Trade360SDK.CustomersApi.Tests/
The SDK is built following these architectural patterns:
IFeedFactory: Creates feed instances with proper configurationISnapshotApiFactory: Creates HTTP clients for snapshot operationsICustomersApiFactory: Creates HTTP clients for customer operations
IEntityHandler<TEntity, TFlow>: Generic interface for message processing- Allows custom handlers for each message type and flow (InPlay/PreMatch)
- Supports dependency injection for testability
- HTTP clients abstract the data access layer
- Clean separation between business logic and transport
- Configuration via
IOptions<T>andIOptionsMonitor<T> - Environment-specific settings management
- Retry policies for transient failures
- Circuit breaker for cascading failures
- Timeout policies for long-running operations
graph LR
subgraph "Application Layer"
SVC[Hosted Service]
HAND[Message Handlers]
end
subgraph "SDK Core"
FEED[IFeed]
SNAP[ISnapshotApiClient]
CUST[ICustomersApiClient]
end
subgraph "Domain Layer"
ENT[Common Entities]
ENUM[Enums]
MSG[Message Types]
end
subgraph "Infrastructure"
RMQ[RabbitMQ Transport]
HTTP[HTTP Transport]
end
SVC --> HAND
HAND --> FEED
SVC --> SNAP
SVC --> CUST
FEED --> ENT
SNAP --> ENT
CUST --> ENT
ENT --> ENUM
ENT --> MSG
FEED --> RMQ
SNAP --> HTTP
CUST --> HTTP
| Decision | Rationale |
|---|---|
| .NET Standard 2.1 | Maximum compatibility across platforms while supporting modern C# features |
| Generic Handler Interface | Type-safe message routing with compile-time checking |
| Factory Pattern | Simplified configuration and lifecycle management |
| Separate Flow Types | Clear separation between InPlay and PreMatch data streams |
| Immutable Entities | Thread-safe data models for concurrent processing |
| HTTP Client Pooling | Efficient socket usage and connection reuse |
Purpose: Shared data models, enums, and message type definitions
Key Classes:
MessageUpdate: Base class for all message typesFixtureMetadataUpdate,LivescoreUpdate,MarketUpdate,SettlementUpdateFixture,Livescore,Market,Bet,ProviderOutrightFixture,OutrightLeague,OutrightMarketTransportMessageHeaders: Message routing metadataWrappedMessage: Message envelope with header/body
Key Enums:
FlowType: InPlay, PreMatchFixtureStatus,BetStatus,MarketType,SettlementTypeGender,AgeCategory,ParticipantTypeVenueAssignment,VenueEnvironment,CourtSurface
Path: src/Trade360SDK.Common.Entities/
Purpose: Core feed abstraction and message routing
Key Interfaces:
IFeed: Main feed interface for start/stop operationsIFeedFactory: Factory for creating feed instancesIEntityHandler<TEntity, TFlow>: Handler interface for processing messages
Key Classes:
InPlay,PreMatch: Flow type markers for generic constraints
Features:
- Message deserialization and routing
- Handler dependency injection
- Flow-based message segregation
- Header extraction and validation
Path: src/Trade360SDK.Feed/
Purpose: RabbitMQ transport implementation for the real-time feed.
Key Classes:
RabbitMqFeed(IFeed): connects to the broker, consumes messages, resolves the queue name, and maps common connection failures to actionableRabbitMqFeedExceptionmessages.RabbitMqFeedFactory(IFeedFactory): creates feed instances fromRmqConnectionSettingsandTrade360Settings.RmqConnectionSettings: host, port, virtual host,UserName,Password,PackageId, optionalCustomQueueName,SslEnabled, prefetch, heartbeat, recovery options.RmqConnectionSettingsValidator: validates settings (including queue naming rules) before connect.RabbitMqSslConfigurator: whenSslEnabledis true, enables TLS on the client and setsSsl.ServerNameto the configured host.
Features:
- TLS (AMQPS) via
SslEnabled; use port 5671 with TLS and 5672 for plain AMQP in typical RabbitMQ setups (see RabbitMQ feed connection). - Optional
CustomQueueName; default consume queue is_{PackageId}_whenCustomQueueNameis empty. - Trims host, virtual host, username, and password before connecting.
- Async message consumption, prefetch and heartbeat configuration, automatic connection recovery.
Path: src/Trade360SDK.Feed.RabbitMQ/
Purpose: HTTP client for snapshot data recovery
Key Interfaces:
ISnapshotInplayApiClient: InPlay snapshot operationsISnapshotPrematchApiClient: PreMatch snapshot operationsISnapshotApiFactory: Factory for creating clients
Available Endpoints:
GetFixtures(): Retrieve fixture snapshotsGetFixtureLivescore(): Get current livescore stateGetFixtureMarkets(): Fetch current market oddsGetFixtureSettlements(): Retrieve settled marketsGetOutrightLeagueEvents(): Outright tournament data
Features:
- Request/response DTOs
- Pagination support
- Filtering by sports, leagues, fixtures, locations
- Automatic retry with Polly
- Response caching support
Path: src/Trade360SDK.SnapshotApi/
Purpose: HTTP client for subscription and metadata management
Key Interfaces:
IPackageDistributionApiClient: Start/stop distributionIMetadataApiClient: Access reference dataISubscriptionApiClient: Manage subscriptionsICustomersApiFactory: Factory for creating clients
Package Distribution Operations:
StartDistribution(): Begin data flowStopDistribution(): Pause data flowGetDistributionStatus(): Check current state
Metadata Operations:
GetSports(),GetLeagues(),GetLocations()GetMarkets(),GetProviders()GetFixtureMetadata(): Fixture details and schedulesGetParticipants(): Participant information with filteringGetVenues(),GetCities(),GetStates(): Geographic data
Subscription Operations:
SubscribeByFixture(),UnsubscribeByFixture()SubscribeByLeague(),UnsubscribeByLeague()SuspendFixture(),UnsuspendFixture()GetQuota(): Check remaining subscription quota
Features:
- Request/response DTOs with validation
- Pagination for large datasets
- Filtering and search capabilities
- Bulk operations support
Path: src/Trade360SDK.CustomersApi/
Purpose: Dependency injection extensions
Key Extensions:
AddT360RmqFeedSdk(): Register feed servicesAddT360ApiClient(): Register HTTP clients
Path: src/Trade360SDK.Microsoft.DependencyInjection/
- Consume live sports data via RabbitMQ with automatic deserialization
- Support for both InPlay and PreMatch data flows
- Type-safe message routing to custom handlers
- Connection recovery on network failures
- Automatic snapshot reconciliation after disconnections
- Heartbeat monitoring and health checks
- Subscribe/unsubscribe by fixture or league
- Manual suspend/unsuspend operations
- Quota tracking and management
- Sports, leagues, locations, markets, participants
- Venue and geographic information
- Participant classification (gender, age, type)
- Async/await throughout for non-blocking operations
- Connection pooling for HTTP clients
- Message prefetch configuration for throughput optimization
- Efficient JSON serialization with System.Text.Json
- Strongly-typed entities and DTOs
- Comprehensive XML documentation
- Working examples for all major features
- Extensive unit and integration test coverage
- Resilience policies via Polly (retry, circuit breaker, timeout)
- Structured logging integration
- Configuration via IOptions pattern
- Health check endpoints support
sequenceDiagram
participant RMQ as RabbitMQ Broker
participant Feed as RabbitMqFeed
participant Router as Message Router
participant Handler as IEntityHandler
participant App as Your Application
RMQ->>Feed: Deliver Message
Feed->>Feed: Extract Headers
Feed->>Feed: Deserialize Body
Feed->>Router: Route by Type + Flow
Router->>Handler: ProcessAsync(entity, headers)
Handler->>App: Business Logic
App-->>Handler: Success
Handler-->>Router: Acknowledgment
Router-->>Feed: ACK/NACK
Feed-->>RMQ: Message Acknowledged
sequenceDiagram
participant App as Your Application
participant Client as HTTP Client
participant Polly as Retry Policy
participant API as Trade360 API
App->>Client: Request Data
Client->>Client: Build Request DTO
Client->>Polly: Execute with Policy
Polly->>API: HTTP Request
API-->>Polly: HTTP Response
alt Success
Polly-->>Client: Response
Client->>Client: Deserialize to DTO
Client-->>App: Return Data
else Transient Failure
Polly->>API: Retry Request
API-->>Polly: HTTP Response
Polly-->>Client: Response or Exception
else Permanent Failure
Polly-->>Client: Throw Exception
Client-->>App: Exception Propagated
end
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /><PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.32" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" /><PackageReference Include="Polly" Version="8.4.2" />
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" /><PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="coverlet.collector" Version="6.0.2" />| Service | Purpose | Authentication |
|---|---|---|
| RabbitMQ Broker | Real-time message streaming | Username/Password |
| Snapshot API | Data recovery and snapshots | Package credentials |
| Customers API | Subscription and metadata | Package credentials |
Before using the SDK, ensure you have:
- .NET SDK 6.0+ installed
- Trade360 package credentials (PackageId, Username, Password)
- Access to Trade360 RabbitMQ broker and HTTP APIs
- (Optional) Docker for running local RabbitMQ instance
# Install all SDK packages
dotnet add package Trade360SDK.Common
dotnet add package Trade360SDK.Feed
dotnet add package Trade360SDK.Feed.RabbitMQ
dotnet add package Trade360SDK.SnapshotApi
dotnet add package Trade360SDK.CustomersApi
dotnet add package Trade360SDK.Microsoft.DependencyInjection# Clone the repository
git clone https://github.com/lsportsltd/trade360-dotnet-sdk.git
cd trade360-dotnet-sdk
# Restore dependencies
dotnet restore
# Build the solution
dotnet build
# Run tests
dotnet test
# Run example project
cd sdk.samples/Trade360SDK.Feed.Example
dotnet runFor plain AMQP (no TLS), use port 5672 and "SslEnabled": false. For TLS, use port 5671 (or the TLS port your broker documents) and "SslEnabled": true.
{
"Trade360": {
"RmqInplaySettings": {
"Host": "your-rabbitmq-host.lsports.eu",
"Port": 5672,
"VirtualHost": "/",
"PackageId": 0,
"CustomQueueName": "",
"SslEnabled": false,
"Username": "your-username",
"Password": "your-password",
"PrefetchCount": 100,
"AutoAck": false,
"RequestedHeartbeatSeconds": 30,
"NetworkRecoveryInterval": 30,
"DispatchConsumersAsync": true,
"AutomaticRecoveryEnabled": true
},
"RmqPrematchSettings": {
"Host": "your-rabbitmq-host.lsports.eu",
"Port": 5672,
"VirtualHost": "/",
"PackageId": 0,
"CustomQueueName": "",
"SslEnabled": false,
"Username": "your-username",
"Password": "your-password",
"PrefetchCount": 100,
"AutoAck": false,
"RequestedHeartbeatSeconds": 30,
"NetworkRecoveryInterval": 30,
"DispatchConsumersAsync": true,
"AutomaticRecoveryEnabled": true
}
},
"Trade360Settings": {
"CustomersApiBaseUrl": "https://stm-api.lsports.eu",
"InplayPackageCredentials": {
"PackageId": 0,
"Username": "your-username",
"Password": "your-password"
},
"PrematchPackageCredentials": {
"PackageId": 0,
"Username": "your-username",
"Password": "your-password"
}
},
"SnapshotInplaySettings": {
"BaseUrl": "https://stm-snapshot.lsports.eu",
"PackageId": 0,
"Username": "your-username",
"Password": "your-password"
},
"SnapshotPrematchSettings": {
"BaseUrl": "https://stm-snapshot.lsports.eu",
"PackageId": 0,
"Username": "your-username",
"Password": "your-password"
}
}using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Trade360SDK.Feed.Configuration;
using Trade360SDK.Common.Configuration;
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
// Configure Feed settings
services.Configure<RmqConnectionSettings>("Inplay",
context.Configuration.GetSection("Trade360:RmqInplaySettings"));
services.Configure<RmqConnectionSettings>("Prematch",
context.Configuration.GetSection("Trade360:RmqPrematchSettings"));
// Configure API settings
services.Configure<Trade360Settings>("CustomerSettings",
context.Configuration.GetSection("Trade360Settings"));
services.Configure<SnapshotApiSettings>("SnapshotInplaySettings",
context.Configuration.GetSection("SnapshotInplaySettings"));
// Register SDK services
services.AddT360RmqFeedSdk(context.Configuration);
services.AddT360ApiClient();
// Register your message handlers
services.AddTrade360Handlers();
// Register your hosted service
services.AddHostedService<YourService>();
})
.Build();
await host.RunAsync();using Trade360SDK.Common.Entities.MessageTypes;
using Trade360SDK.Common.Entities.Enums;
using Trade360SDK.Common.Models;
using Trade360SDK.Feed.Interfaces;
public class LivescoreUpdateHandlerInplay : IEntityHandler<LivescoreUpdate, InPlay>
{
private readonly ILogger<LivescoreUpdateHandlerInplay> _logger;
public LivescoreUpdateHandlerInplay(ILogger<LivescoreUpdateHandlerInplay> logger)
{
_logger = logger;
}
public Task ProcessAsync(LivescoreUpdate entity, TransportMessageHeaders headers,
CancellationToken cancellationToken = default)
{
_logger.LogInformation("Received livescore update for fixture {FixtureId}",
headers.FixtureId);
foreach (var lsEvent in entity.Events ?? Enumerable.Empty<LivescoreEvent>())
{
_logger.LogInformation("Fixture {FixtureId}: Score {Score}",
lsEvent.Fixture?.Id,
lsEvent.Livescore?.Scoreboard?.Results?.FirstOrDefault()?.Value);
}
return Task.CompletedTask;
}
}public static class ServiceCollectionExtensions
{
public static IServiceCollection AddTrade360Handlers(this IServiceCollection services)
{
// InPlay handlers
services
.AddScoped<IEntityHandler<FixtureMetadataUpdate, InPlay>, FixtureMetadataUpdateHandlerInplay>()
.AddScoped<IEntityHandler<LivescoreUpdate, InPlay>, LivescoreUpdateHandlerInplay>()
.AddScoped<IEntityHandler<MarketUpdate, InPlay>, MarketUpdateHandlerInplay>()
.AddScoped<IEntityHandler<SettlementUpdate, InPlay>, SettlementUpdateHandlerInplay>()
.AddScoped<IEntityHandler<HeartbeatUpdate, InPlay>, HeartbeatHandlerInplay>()
.AddScoped<IEntityHandler<KeepAliveUpdate, InPlay>, KeepAliveUpdateHandlerInplay>();
// PreMatch handlers
services
.AddScoped<IEntityHandler<FixtureMetadataUpdate, PreMatch>, FixtureMetadataUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<LivescoreUpdate, PreMatch>, LivescoreUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<MarketUpdate, PreMatch>, MarketUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<SettlementUpdate, PreMatch>, SettlementUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<OutrightFixtureUpdate, PreMatch>, OutrightFixtureUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<OutrightLeagueUpdate, PreMatch>, OutrightLeagueUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<OutrightFixtureMarketUpdate, PreMatch>, OutrightFixtureMarketUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<OutrightLeagueMarketUpdate, PreMatch>, OutrightLeagueMarketUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<OutrightSettlementsUpdate, PreMatch>, OutrightSettlementsUpdateHandlerPrematch>()
.AddScoped<IEntityHandler<OutrightScoreUpdate, PreMatch>, OutrightScoreUpdateHandlerPrematch>();
return services;
}
}using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Trade360SDK.Feed.Configuration;
using Trade360SDK.Feed.Interfaces;
using Trade360SDK.Common.Configuration;
using Trade360SDK.Common.Entities.Enums;
public class YourService : IHostedService
{
private readonly IFeed? _inplayFeed;
private readonly IFeed? _prematchFeed;
private readonly ILogger<YourService> _logger;
public YourService(
IFeedFactory feedFactory,
IOptionsMonitor<RmqConnectionSettings> rmqSettingsMonitor,
IOptionsMonitor<Trade360Settings> customerSettingsMonitor,
ILogger<YourService> logger)
{
_logger = logger;
var inplaySettings = rmqSettingsMonitor.Get("Inplay");
var prematchSettings = rmqSettingsMonitor.Get("Prematch");
var customerSettings = customerSettingsMonitor.Get("CustomerSettings");
_inplayFeed = feedFactory.CreateFeed(inplaySettings, customerSettings, FlowType.InPlay);
_prematchFeed = feedFactory.CreateFeed(prematchSettings, customerSettings, FlowType.PreMatch);
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting Trade360 feeds...");
// Start feeds
if (_inplayFeed != null)
await _inplayFeed.StartAsync(connectAtStart: true, cancellationToken);
if (_prematchFeed != null)
await _prematchFeed.StartAsync(connectAtStart: true, cancellationToken);
_logger.LogInformation("Trade360 feeds started successfully");
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping Trade360 feeds...");
if (_inplayFeed != null)
await _inplayFeed.StopAsync(cancellationToken);
if (_prematchFeed != null)
await _prematchFeed.StopAsync(cancellationToken);
_logger.LogInformation("Trade360 feeds stopped successfully");
}
}using Trade360SDK.CustomersApi.Interfaces;
using Trade360SDK.CustomersApi.Entities.SubscriptionApi.Requests;
public class SubscriptionExample
{
private readonly ISubscriptionApiClient _subscriptionClient;
public async Task SubscribeToFixtures(int[] fixtureIds)
{
var request = new FixtureSubscriptionRequestDto
{
Fixtures = fixtureIds
};
var response = await _subscriptionClient.SubscribeByFixture(request);
Console.WriteLine($"Subscribed to {response.Fixtures.Count} fixtures");
}
}using Trade360SDK.SnapshotApi.Interfaces;
using Trade360SDK.SnapshotApi.Entities.Requests;
public class SnapshotExample
{
private readonly ISnapshotInplayApiClient _snapshotClient;
public async Task GetFixtureSnapshot(int fixtureId)
{
var request = new GetFixturesRequestDto
{
Fixtures = new List<int> { fixtureId }
};
var fixtures = await _snapshotClient.GetFixtures(request);
foreach (var fixture in fixtures)
{
Console.WriteLine($"Fixture: {fixture.Fixture.Id} - {fixture.Fixture.Name}");
}
}
}using Trade360SDK.CustomersApi.Interfaces;
using Trade360SDK.CustomersApi.Entities.MetadataApi.Requests;
public class MetadataExample
{
private readonly IMetadataApiClient _metadataClient;
public async Task GetSports()
{
var sports = await _metadataClient.GetSportsAsync();
foreach (var sport in sports)
{
Console.WriteLine($"Sport: {sport.Id} - {sport.Name}");
}
}
public async Task GetParticipants(int sportId)
{
var request = new GetParticipantsRequestDto
{
Filters = new ParticipantFilterDto
{
SportIds = new[] { sportId },
Gender = Gender.Men,
Type = ParticipantType.Club
},
Page = 1,
PageSize = 50
};
var response = await _metadataClient.GetParticipantsAsync(request);
Console.WriteLine($"Total participants: {response.TotalItems}");
foreach (var participant in response.Data)
{
Console.WriteLine($"Participant: {participant.Id} - {participant.Name}");
}
}
}The SDK supports multiple environments through configuration:
{
"Trade360": {
"RmqInplaySettings": {
"Host": "dev-rabbitmq.lsports.eu",
...
}
}
}{
"Trade360": {
"RmqInplaySettings": {
"Host": "qa-rabbitmq.lsports.eu",
...
}
}
}{
"Trade360": {
"RmqInplaySettings": {
"Host": "prod-rabbitmq.lsports.eu",
...
}
}
}FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["YourApp/YourApp.csproj", "YourApp/"]
RUN dotnet restore "YourApp/YourApp.csproj"
COPY . .
WORKDIR "/src/YourApp"
RUN dotnet build "YourApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "YourApp.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "YourApp.dll"]apiVersion: apps/v1
kind: Deployment
metadata:
name: trade360-consumer
spec:
replicas: 3
selector:
matchLabels:
app: trade360-consumer
template:
metadata:
labels:
app: trade360-consumer
spec:
containers:
- name: trade360-consumer
image: your-registry/trade360-consumer:latest
env:
- name: Trade360__RmqInplaySettings__Host
valueFrom:
secretKeyRef:
name: trade360-secrets
key: rmq-host
- name: Trade360__RmqInplaySettings__Username
valueFrom:
secretKeyRef:
name: trade360-secrets
key: rmq-username
- name: Trade360__RmqInplaySettings__Password
valueFrom:
secretKeyRef:
name: trade360-secrets
key: rmq-password
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"The SDK supports configuration via environment variables using the standard ASP.NET Core convention:
# RabbitMQ Configuration
export Trade360__RmqInplaySettings__Host="your-host"
export Trade360__RmqInplaySettings__Port=5672
export Trade360__RmqInplaySettings__SslEnabled=false
export Trade360__RmqInplaySettings__Username="your-username"
export Trade360__RmqInplaySettings__Password="your-password"
# API Configuration
export Trade360Settings__CustomersApiBaseUrl="https://stm-api.lsports.eu"
export Trade360Settings__InplayPackageCredentials__PackageId=123The SDK includes comprehensive test coverage:
test/
βββ Trade360SDK.Common.Entities.Tests/ # Entity and enum tests
βββ Trade360SDK.Feed.Tests/ # Feed abstraction tests
βββ Trade360SDK.Feed.RabbitMQ.Tests/ # RabbitMQ implementation tests
βββ Trade360SDK.SnapshotApi.Tests/ # Snapshot API tests
βββ Trade360SDK.CustomersApi.Tests/ # Customers API tests
# Run all tests
dotnet test
# Run tests with code coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
# Run specific test project
dotnet test test/Trade360SDK.Feed.Tests/
# Run tests with filter
dotnet test --filter "Category=Unit"
# Run tests with detailed output
dotnet test --logger "console;verbosity=detailed"- Unit Tests: Fast, isolated tests with mocked dependencies
- Integration Tests: Tests with actual HTTP clients and message brokers
- Component Tests: End-to-end tests of SDK components
using Xunit;
using FluentAssertions;
using Trade360SDK.Common.Entities.MessageTypes;
public class LivescoreUpdateTests
{
[Fact]
public void LivescoreUpdate_Should_Have_Events_Property()
{
// Arrange & Act
var update = new LivescoreUpdate
{
Events = new List<LivescoreEvent>()
};
// Assert
update.Events.Should().NotBeNull();
update.Events.Should().BeEmpty();
}
}services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Debug);
});Or via appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Trade360SDK": "Debug",
"RabbitMQ.Client": "Warning"
}
}
}RabbitMQ.Client.Exceptions.BrokerUnreachableException: None of the specified endpoints were reachable
Solution:
- Verify RabbitMQ host and port are correct
- TLS vs port: with
SslEnabled: true, use the brokerβs TLS port (often 5671). WithSslEnabled: false, use the plain AMQP port (often 5672). Mixing them produces errors such as βCannot determine the frame sizeβ or unreachable broker; the SDK may wrap these inRabbitMqFeedExceptionwith a hint when the port matches the usual mismatch pattern - Check firewall rules
- Ensure RabbitMQ service is running
- Verify credentials and virtual host access (see below)
Solution:
- Confirm
UserName,Password, andVirtualHostinRmqConnectionSettingsmatch the broker. The SDK maps authentication failures to aRabbitMqFeedExceptionwith guidance; this is separate from queue naming and TLS port issues
Solution:
- Ensure handler is registered in DI container
- Check handler implements correct interface:
IEntityHandler<TEntity, TFlow> - Verify flow type matches (InPlay vs PreMatch)
- Check message type attribute on entity class
System.Text.Json.JsonException: The JSON value could not be converted
Solution:
- Check entity properties match incoming JSON structure
- Verify nullable annotations are correct
- Enable detailed JSON exception logging
View RabbitMQ Management UI:
# Access RabbitMQ management interface
http://localhost:15672
# Default credentials: guest/guestMonitor Message Flow:
services.AddLogging(builder =>
{
builder.AddFilter("Trade360SDK.Feed.RabbitMQ", LogLevel.Trace);
});Inspect Message Headers:
public Task ProcessAsync(LivescoreUpdate entity, TransportMessageHeaders headers,
CancellationToken cancellationToken = default)
{
_logger.LogDebug("Message Type: {Type}, Sequence: {Seq}, GUID: {Guid}",
headers.MessageType,
headers.MessageSequence,
headers.MessageGuid);
// Your logic here
}The SDK uses Microsoft.Extensions.Logging for structured logging:
_logger.LogInformation(
"Feed started for package {PackageId} on flow {Flow}",
packageId,
flowType);Implement health checks for your application:
services.AddHealthChecks()
.AddCheck<RabbitMQHealthCheck>("rabbitmq")
.AddCheck<SnapshotApiHealthCheck>("snapshot-api")
.AddCheck<CustomersApiHealthCheck>("customers-api");Track SDK performance:
// Message processing metrics
_metrics.IncrementCounter("messages_processed", 1,
new[] { new KeyValuePair<string, object>("type", "livescore") });
// Connection metrics
_metrics.RecordHistogram("connection_duration_ms", connectionDuration.TotalMilliseconds);
// Error metrics
_metrics.IncrementCounter("processing_errors", 1,
new[] { new KeyValuePair<string, object>("error_type", ex.GetType().Name) });The SDK supports distributed tracing:
services.AddOpenTelemetry()
.WithTracing(builder => builder
.AddSource("Trade360SDK.Feed")
.AddSource("Trade360SDK.SnapshotApi")
.AddSource("Trade360SDK.CustomersApi"));- Command-line arguments
- Environment variables
appsettings.{Environment}.jsonappsettings.json- Default values
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Trade360SDK": "Debug"
}
},
"Trade360": {
"RmqInplaySettings": {
"Host": "rabbitmq.lsports.eu",
"Port": 5672,
"VirtualHost": "/",
"PackageId": 12345,
"CustomQueueName": "",
"SslEnabled": false,
"Username": "your-username",
"Password": "your-password",
"PrefetchCount": 100,
"AutoAck": false,
"RequestedHeartbeatSeconds": 30,
"NetworkRecoveryInterval": 30,
"DispatchConsumersAsync": true,
"AutomaticRecoveryEnabled": true
},
"RmqPrematchSettings": {
"Host": "rabbitmq.lsports.eu",
"Port": 5672,
"VirtualHost": "/",
"PackageId": 12346,
"CustomQueueName": "",
"SslEnabled": false,
"Username": "your-username",
"Password": "your-password",
"PrefetchCount": 50,
"AutoAck": false,
"RequestedHeartbeatSeconds": 30,
"NetworkRecoveryInterval": 30,
"DispatchConsumersAsync": true,
"AutomaticRecoveryEnabled": true
}
},
"Trade360Settings": {
"CustomersApiBaseUrl": "https://stm-api.lsports.eu",
"InplayPackageCredentials": {
"PackageId": 12345,
"Username": "your-username",
"Password": "your-password"
},
"PrematchPackageCredentials": {
"PackageId": 12346,
"Username": "your-username",
"Password": "your-password"
}
},
"SnapshotInplaySettings": {
"BaseUrl": "https://stm-snapshot.lsports.eu",
"PackageId": 12345,
"Username": "your-username",
"Password": "your-password",
"TimeoutSeconds": 30,
"RetryCount": 3
},
"SnapshotPrematchSettings": {
"BaseUrl": "https://stm-snapshot.lsports.eu",
"PackageId": 12346,
"Username": "your-username",
"Password": "your-password",
"TimeoutSeconds": 30,
"RetryCount": 3
}
}Settings are bound to RmqConnectionSettings (for example from Trade360:RmqInplaySettings and Trade360:RmqPrematchSettings). The feed implementation is RabbitMqFeed in Trade360SDK.Feed.RabbitMQ.
| Mode | SslEnabled |
Typical Port |
Constants on RabbitMqFeed |
|---|---|---|---|
| Plain AMQP | false |
5672 | StandardAmqpPlainPort |
| TLS (AMQPS) | true |
5671 | StandardAmqpTlsPort |
RabbitMQ commonly listens for plain AMQP on 5672 and TLS on 5671. Your brokerβs documentation may differ.
When TLS is enabled, the client sets Ssl.Enabled and Ssl.ServerName to the configured Host (see RabbitMqSslConfigurator). Trust and hostname verification follow .NET / OS defaults unless you extend the client elsewhere.
If SslEnabled and Port are inconsistent (for example TLS enabled on 5672, or TLS disabled on 5671) and the connection fails with BrokerUnreachableException, RabbitMqFeed throws RabbitMqFeedException with a message that explains the usual fix (enable TLS and use the TLS port, or disable TLS and use the plain port).
- If
CustomQueueNameis set to a non-empty value (after trim), the consumer uses that queue name. - If
CustomQueueNameis omitted or empty, the consumer uses_{PackageId}_, which requiresPackageId> 0. - If
PackageIdis 0, you must setCustomQueueName; validation rejectsPackageId == 0with no custom queue. PackageIdmust not be negative. The effective queue name length must not exceedRabbitMqFeed.ConsumeQueueNameMaxLength(255 characters).
The resolved name is available as RabbitMqFeed.ResolveConsumeQueueName(RmqConnectionSettings) and is validated by RmqConnectionSettingsValidator.
- Host, VirtualHost, UserName, and Password are trimmed before use (leading/trailing spaces often break PLAIN login or vhost matching).
- JSON configuration keys are case-insensitive;
UsernameandUserNameboth bind toUserName.
- Authentication failures (
AuthenticationFailureException) are wrapped inRabbitMqFeedExceptionwith a message focused on credentials and virtual host access (not queue naming). - TLS failures when
SslEnabledis true may be wrapped with guidance about port, certificate trust, and hostname (SAN/CN). - After a successful start, an information log entry includes the resolved queue name, host, virtual host, and whether SSL is enabled.
We welcome contributions to the Trade360 .NET SDK! Please follow these guidelines:
- Code Style: Follow C# coding conventions and .NET best practices
- Testing: All new features must include unit tests with >80% coverage
- Documentation: Update XML documentation comments for public APIs
- Changelog: Add entries to
CHANGELOG.mdfollowing Keep a Changelog format
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes with appropriate tests
- Run tests:
dotnet test - Update documentation if needed
- Commit your changes:
git commit -m "feat: add your feature" - Push to your fork:
git push origin feature/your-feature-name - Create a Pull Request
Follow Conventional Commits:
feat: add new message type support
fix: resolve connection timeout issue
docs: update API documentation
test: add integration tests for snapshot API
refactor: simplify handler registration
This project is licensed under the MIT License - see the LICENSE file for details.
- Documentation: https://docs.lsports.eu
- API Reference: https://docs.lsports.eu/api
- This repository β RabbitMQ feed settings (TLS, ports, queues): see RabbitMQ feed connection
- Support: support@lsports.eu
- GitHub Repository: https://github.com/lsportsltd/trade360-dotnet-sdk
- NuGet Packages: https://www.nuget.org/profiles/LSports
For technical support and questions:
- Email: support@lsports.eu
- Documentation: https://docs.lsports.eu
- GitHub Issues: https://github.com/lsportsltd/trade360-dotnet-sdk/issues
Made with β€οΈ by LSports