Skip to content

Commit 618cd57

Browse files
authored
Merge pull request #54 from visualHFT/sdk-market-connector
Added new SDK template project to create new market connectors
2 parents cfa1217 + 3403265 commit 618cd57

File tree

10 files changed

+514
-27
lines changed

10 files changed

+514
-27
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<!--
4+
VisualHFT plug‑ins are simple .NET class libraries targeting the
5+
Windows desktop. They reference VisualHFT.Commons to gain access to
6+
base classes such as BasePluginDataRetriever, and they may rely on
7+
additional third‑party packages to speak to specific exchanges.
8+
-->
9+
<PropertyGroup>
10+
<!-- Target .NET 8 on Windows. The UseWPF flag is required by
11+
VisualHFT even though plug‑ins themselves do not create windows. -->
12+
<TargetFramework>net8.0-windows8.0</TargetFramework>
13+
<Nullable>enable</Nullable>
14+
<UseWPF>true</UseWPF>
15+
<SupportedOSPlatformVersion>8.0</SupportedOSPlatformVersion>
16+
</PropertyGroup>
17+
18+
<!-- Include any sample message files or other resources here. These
19+
directives copy the files to the output directory so the plug‑in can
20+
read them at runtime. Remove this ItemGroup if not needed. -->
21+
<ItemGroup>
22+
<!-- <Content Include="sample_messages\Scenario1.json"> -->
23+
<!-- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> -->
24+
<!-- </Content> -->
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<!-- Add a reference to a client library for your exchange here. For
29+
example, Binance.Net, KrakenExchange.Net, CoinbaseAdvancedTrade.Net.
30+
Remove this line if you plan to implement your own WebSocket/REST
31+
client. -->
32+
<!-- <PackageReference Include="ExchangeApi.Net" Version="1.0.0" /> -->
33+
<PackageReference Include="log4net" Version="3.1.0" />
34+
</ItemGroup>
35+
36+
<ItemGroup>
37+
<!-- All plug‑ins must reference VisualHFT.Commons. Adjust the
38+
relative path based on where this template lives in your solution. -->
39+
<ProjectReference Include="..\..\VisualHFT.Commons\VisualHFT.Commons.csproj" />
40+
</ItemGroup>
41+
42+
<!-- WPF UI files. The settings control is included as a Page so
43+
it can be discovered and rendered by VisualHFT's plug‑in manager.
44+
The code-behind is compiled as a normal C# file. -->
45+
<ItemGroup>
46+
<Page Include="UserControls\PluginSettingsView.xaml">
47+
<Generator>MSBuild:Compile</Generator>
48+
<SubType>Designer</SubType>
49+
</Page>
50+
<Compile Include="UserControls\PluginSettingsView.xaml.cs" />
51+
</ItemGroup>
52+
53+
</Project>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# MarketConnector Template
2+
3+
This directory contains a skeleton project that can be used as a starting
4+
point for building new market‑data connectors for **VisualHFT**. The
5+
structure and file names mirror the existing crypto connectors (e.g.
6+
`MarketConnectors.Binance`, `MarketConnectors.Kraken`) found in the
7+
`VisualHFT.Plugins` solution folder. To create your own connector:
8+
9+
1. **Rename the project**: Replace `MarketConnectorTemplate` and the
10+
namespace `MarketConnectorTemplate` with a name that reflects your
11+
exchange (e.g. `MarketConnectors.MyExchange`). Update the
12+
`<AssemblyName>` and `<RootNamespace>` in the `.csproj` if desired.
13+
2. **Reference an exchange client**: Add an appropriate
14+
`<PackageReference>` to the `.csproj` so you can talk to the target
15+
venue. For example, Binance connectors use `Binance.Net` and Kraken
16+
connectors use `KrakenExchange.Net`.
17+
3. **Implement connection logic**: Fill in the TODO sections of
18+
`TemplatePlugin.cs` to instantiate your client, subscribe to
19+
orderbook and trade streams, and convert the received messages into
20+
`VisualHFT.Commons.Model.OrderBook` and `VisualHFT.Commons.Model.Trade`
21+
objects. Use `RaiseOnDataReceived()` to publish these objects back
22+
into VisualHFT.
23+
4. **Expose settings**: Customize `TemplateSettings.cs` with any
24+
configuration values you need (API credentials, symbol lists,
25+
endpoints). These settings will appear in the VisualHFT settings UI.
26+
5. **Implement a settings page**: A simple WPF user control is provided
27+
in `UserControls/PluginSettingsView.xaml`. The control is bound to
28+
the properties on `TemplateSettings` and includes basic text boxes for
29+
API keys, symbol lists, depth levels and aggregation level. You can
30+
edit this XAML to add additional fields or instructions. The
31+
corresponding code‑behind (`PluginSettingsView.xaml.cs`) handles
32+
hyperlink navigation. When VisualHFT loads your plug‑in it will
33+
automatically display this control when the user edits the plug‑in
34+
settings.
35+
6. **Build and deploy**: Compile the project. The resulting DLL will be
36+
auto‑discovered by the VisualHFT plug‑in loader when placed in
37+
the `plugins` folder. If you include additional XAML files or
38+
resources, ensure that the `.csproj` file lists them in an
39+
`<ItemGroup>` so they are compiled correctly.
40+
41+
For detailed guidance on how to extend VisualHFT, see the
42+
`MarketConnectorSDK_Guidelines.md` document in the repository root. It
43+
explains the life‑cycle of a plug‑in, how settings are managed and how
44+
data flows through the system.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using VisualHFT.Commons.Helpers;
4+
using VisualHFT.Enums;
5+
using VisualHFT.Model;
6+
using VisualHFT.PluginManager;
7+
using VisualHFT.UserSettings;
8+
9+
namespace TemplateExchangePlugin
10+
{
11+
/// <summary>
12+
/// Template for a market connector plugin. Implement IPlugin and derive from BasePluginDataRetriever
13+
/// to receive market data and push it into the VisualHFT helper classes.
14+
/// </summary>
15+
public class TemplateExchangePlugin : VisualHFT.Commons.PluginManager.BasePluginDataRetriever
16+
{
17+
public TemplateExchangePlugin()
18+
{
19+
Name = "TemplateExchange";
20+
Description = "Connects to TemplateExchange and streams order book and trade data.";
21+
Author = "Developer Name";
22+
Version = "0.0.1";
23+
Settings = new TemplateExchangeSettings();
24+
}
25+
26+
/// <summary>
27+
/// Start the plugin asynchronously. Initialize connections to the exchange API here.
28+
/// Use Settings values such as API keys, symbols, depth levels, etc.
29+
/// </summary>
30+
public override async Task StartAsync()
31+
{
32+
Status = ePluginStatus.STARTING;
33+
// TODO: Initialize your API client here using Settings.ApiKey, Settings.ApiSecret, etc.
34+
// Subscribe to order book updates and trades. For each update call RaiseOnDataReceived with
35+
// the appropriate OrderBook or Trade model. Set provider status via RaiseOnProviderStatusChanged.
36+
Status = ePluginStatus.STARTED;
37+
}
38+
39+
/// <summary>
40+
/// Stop the plugin asynchronously. Close connections and clean up resources.
41+
/// </summary>
42+
public override async Task StopAsync()
43+
{
44+
Status = ePluginStatus.STOPPING;
45+
// TODO: Disconnect from the exchange API and dispose of resources.
46+
Status = ePluginStatus.STOPPED;
47+
}
48+
49+
/// <summary>
50+
/// Initialize default settings for the plugin. Invoked on construction.
51+
/// </summary>
52+
protected override void InitializeDefaultSettings()
53+
{
54+
Settings = new TemplateExchangeSettings
55+
{
56+
ApiKey = string.Empty,
57+
ApiSecret = string.Empty,
58+
Symbols = "BTC-USD", // comma-separated list of symbols
59+
DepthLevels = 20
60+
};
61+
}
62+
}
63+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.ComponentModel;
2+
using VisualHFT.Enums;
3+
using VisualHFT.UserSettings;
4+
5+
namespace TemplateExchangePlugin
6+
{
7+
/// <summary>
8+
/// Settings class for TemplateExchange plugin. Extend ISetting to expose
9+
/// configurable properties that the user can set via the settings UI.
10+
/// </summary>
11+
public class TemplateExchangeSettings : ISetting
12+
{
13+
[Description("API key issued by the exchange")]
14+
public string ApiKey { get; set; }
15+
16+
[Description("API secret issued by the exchange")]
17+
public string ApiSecret { get; set; }
18+
19+
[Description("Symbols to subscribe, comma-separated (e.g., BTC-USD,ETH-USD)")]
20+
public string Symbols { get; set; }
21+
22+
[Description("Depth levels to request for the order book")]
23+
public int DepthLevels { get; set; }
24+
25+
[Description("Aggregation level for studies (time in ms or event count)")]
26+
public int AggregationLevel { get; set; } = 1;
27+
28+
[Description("License level required to use this plugin")]
29+
public eLicenseLevel LicenseLevel { get; set; } = eLicenseLevel.COMMUNITY;
30+
}
31+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using VisualHFT.Commons.PluginManager;
5+
using VisualHFT.Enums;
6+
using VisualHFT.Commons.Model;
7+
using VisualHFT.Commons.Interfaces;
8+
9+
namespace MarketConnectorTemplate
10+
{
11+
/// <summary>
12+
/// Skeleton implementation of a market data connector. To build a real
13+
/// connector, derive from BasePluginDataRetriever and implement the
14+
/// connection and subscription logic using your chosen exchange client
15+
/// library. This class provides overridable methods for starting and
16+
/// stopping the plug‑in and illustrates how to publish orderbook and
17+
/// trade events into VisualHFT.
18+
/// </summary>
19+
public class TemplatePlugin : BasePluginDataRetriever
20+
{
21+
private TemplateSettings _settings;
22+
23+
/// <inheritdoc />
24+
public override string Name { get; set; } = "Template Exchange Plugin";
25+
/// <inheritdoc />
26+
public override string Version { get; set; } = "0.1.0";
27+
/// <inheritdoc />
28+
public override string Description { get; set; } = "Skeleton connector for a generic exchange.";
29+
/// <inheritdoc />
30+
public override string Author { get; set; } = "VisualHFT Community";
31+
/// <inheritdoc />
32+
public override ISetting Settings { get => _settings; set => _settings = (TemplateSettings)value; }
33+
/// <inheritdoc />
34+
public override Action CloseSettingWindow { get; set; }
35+
36+
public TemplatePlugin()
37+
{
38+
// Set the reconnection action to automatically restart the connector
39+
// when a connection drop is detected. If your exchange client
40+
// exposes reconnection hooks you may override this to use them instead.
41+
SetReconnectionAction(InternalStartAsync);
42+
}
43+
44+
/// <summary>
45+
/// Called by the plug‑in manager when the user starts this plug‑in.
46+
/// Establishes the connection to the exchange and subscribes to
47+
/// orderbook and trade feeds for the configured symbols. Always
48+
/// call base.StartAsync() before performing your own logic so that
49+
/// VisualHFT can update the provider status correctly.
50+
/// </summary>
51+
public override async Task StartAsync()
52+
{
53+
await base.StartAsync();
54+
55+
// TODO: Instantiate your exchange client here using _settings. For
56+
// example, Binance.Net, Bitfinex.Net, Kraken.Net, etc. Many
57+
// libraries accept API credentials via constructor parameters.
58+
59+
try
60+
{
61+
await InternalStartAsync();
62+
if (Status == ePluginStatus.STOPPED_FAILED)
63+
return;
64+
65+
// Inform VisualHFT that the provider is connected
66+
RaiseOnDataReceived(GetProviderModel(eSESSIONSTATUS.CONNECTED));
67+
Status = ePluginStatus.STARTED;
68+
}
69+
catch (Exception ex)
70+
{
71+
// If initialization fails, propagate the error to the base class
72+
// so that reconnection logic can kick in.
73+
await HandleConnectionLost(ex.Message, ex);
74+
}
75+
}
76+
77+
/// <summary>
78+
/// Contains your actual startup logic. Do not call this directly
79+
/// outside of StartAsync(). It is used by the base class to
80+
/// implement automatic reconnection.
81+
/// </summary>
82+
private async Task InternalStartAsync()
83+
{
84+
// Loop over all configured symbols and subscribe. You may want
85+
// to normalise symbols here by calling GetNormalizedSymbol().
86+
foreach (string symbol in GetAllNormalizedSymbols())
87+
{
88+
// TODO: Replace this with calls to your exchange client
89+
// For example:
90+
// await client.SubscribeToOrderBookUpdatesAsync(symbol, _settings.DepthLevels, OnOrderBookUpdate);
91+
// await client.SubscribeToTradeUpdatesAsync(symbol, OnTradeUpdate);
92+
}
93+
await Task.CompletedTask;
94+
}
95+
96+
/// <summary>
97+
/// Called by the plug‑in manager when the user stops this plug‑in.
98+
/// Unsubscribes from all feeds and cleans up resources.
99+
/// </summary>
100+
public override async Task StopAsync()
101+
{
102+
Status = ePluginStatus.STOPPING;
103+
// TODO: Unsubscribe your exchange client and dispose resources here
104+
105+
// Notify VisualHFT that the provider is disconnected and clear the
106+
// orderbook snapshot for this provider.
107+
RaiseOnDataReceived(new List<OrderBook>());
108+
RaiseOnDataReceived(GetProviderModel(eSESSIONSTATUS.DISCONNECTED));
109+
await base.StopAsync();
110+
}
111+
112+
/// <summary>
113+
/// Handler for orderbook updates from the exchange. Convert the raw
114+
/// payload into VisualHFT.Model.OrderBook and publish it via
115+
/// RaiseOnDataReceived(). Note that OrderBook has a Provider
116+
/// property which should be set to Name and a Symbol property
117+
/// containing the normalised symbol.
118+
/// </summary>
119+
/// <param name="update">Raw exchange payload.</param>
120+
private void OnOrderBookUpdate(object update)
121+
{
122+
// TODO: Parse update into a VisualHFT.Model.OrderBook instance. At
123+
// minimum you should populate Bids, Asks, Symbol, Provider and
124+
// Timestamp. Use GetNormalizedSymbol() to map the exchange
125+
// symbol into your configured symbol list.
126+
127+
// Example stub (remove once implemented):
128+
var orderBook = new OrderBook
129+
{
130+
Provider = Name,
131+
// Symbol = GetNormalizedSymbol(rawSymbol),
132+
// Bids = new List<OrderBookLevel> { ... },
133+
// Asks = new List<OrderBookLevel> { ... },
134+
// Timestamp = DateTime.UtcNow
135+
};
136+
RaiseOnDataReceived(orderBook);
137+
}
138+
139+
/// <summary>
140+
/// Handler for trade updates from the exchange. Convert the raw
141+
/// payload into VisualHFT.Model.Trade and publish via
142+
/// RaiseOnDataReceived().
143+
/// </summary>
144+
/// <param name="update">Raw trade payload.</param>
145+
private void OnTradeUpdate(object update)
146+
{
147+
// TODO: Parse update into a VisualHFT.Model.Trade instance. A trade
148+
// must set Symbol, Price, Size, IsBuyerMaker and Timestamp.
149+
150+
// Example stub (remove once implemented):
151+
var trade = new Trade
152+
{
153+
Provider = Name,
154+
// Symbol = GetNormalizedSymbol(rawSymbol),
155+
// Price = rawTrade.Price,
156+
// Size = rawTrade.Quantity,
157+
// IsBuyerMaker = rawTrade.IsBuyerMaker,
158+
// Timestamp = DateTime.UtcNow
159+
};
160+
RaiseOnDataReceived(trade);
161+
}
162+
}
163+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using VisualHFT.Commons.Interfaces;
2+
using System.ComponentModel;
3+
4+
namespace MarketConnectorTemplate
5+
{
6+
/// <summary>
7+
/// Settings class for the template market connector. Each field is
8+
/// categorized so it will appear neatly grouped in the VisualHFT
9+
/// configuration UI. You can extend this class to include any
10+
/// additional exchange‑specific parameters (for example, API endpoint
11+
/// overrides, authentication scopes, etc.).
12+
/// </summary>
13+
public class TemplateSettings : ISetting
14+
{
15+
[Category("Connection")]
16+
[Description("API key used for authenticated requests (leave empty for public data).")]
17+
public string ApiKey { get; set; } = string.Empty;
18+
19+
[Category("Connection")]
20+
[Description("API secret used for authenticated requests (leave empty for public data).")]
21+
public string ApiSecret { get; set; } = string.Empty;
22+
23+
[Category("General")]
24+
[Description("Comma‑separated list of symbols to subscribe to. Use native exchange notation (e.g. BTCUSDT).")]
25+
public string Symbols { get; set; } = "BTCUSDT";
26+
27+
[Category("Market Data")]
28+
[Description("Depth of the orderbook to request. For example, 10 requests the top 10 bids/asks.")]
29+
public int DepthLevels { get; set; } = 10;
30+
31+
[Category("Performance")]
32+
[Description("Aggregation window in milliseconds for orderbook events. 0 means no aggregation.")]
33+
public int AggregationLevelMs { get; set; } = 0;
34+
}
35+
}

0 commit comments

Comments
 (0)