SSL Buy Side Sample
Source code
using System;
using System.CommandLine;
using OnixS.Fix;
using System.Globalization;
using System.IO;
using OnixS.Fix.Fix44;
using System.Net;
using NLog.Extensions.Logging;
namespace SslBuySide
{
/// <summary>
/// Establishes the FIX session as Initiator.
/// </summary>
internal static class Initiator
{
private const ProtocolVersion fixVersion = ProtocolVersion.Fix44;
private static string GetLicenseStoreFolder()
{
string path = Path.Join(AppContext.BaseDirectory, "../../../../../license");
if (Directory.Exists(path))
return path;
// expecting it run after `dotnet publish` using default paths
return Path.Join(AppContext.BaseDirectory, "../../../../../../license");
}
private static System.Security.SecureString MakeSecureString(string input)
{
System.Security.SecureString result = new();
foreach (char c in input)
result.AppendChar(c);
return result;
}
private static Message CreateOrderMessage(IMessageInfoDictionary dictionary)
{
Message order = new(MsgType.NewOrderSingle, dictionary);
order.Set(Tag.HandlInst, HandlInst.AutoExecPub)
.Set(Tag.ClOrdID, "Unique identifier for Order")
.Set(Tag.Symbol, "TSLA")
.Set(Tag.Side, Side.Buy)
.Set(Tag.OrderQty, 1000)
.Set(Tag.OrdType, OrdType.Market)
.Set(Tag.TransactTime, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss", CultureInfo.InvariantCulture));
#if DEBUG
order.Validate();
#endif
return order;
}
static bool AskForAcceptingCertificate(bool defaultValue)
{
while (true)
{
string defaultAnswer = defaultValue ? "yes" : "no";
Console.Write($"\nAccept this certificate? Y[es]/N[o] (blank for {defaultAnswer}): ");
switch (Console.ReadLine()?.ToUpper())
{
case "Y": case "YES": return true;
case "N": case "NO": return false;
case "": case null: return defaultValue;
}
}
}
private static void Run(bool usePemCertificate, bool verifyPeer, bool interactiveCertValidation)
{
Console.WriteLine("SSL Buy Side Sample");
EngineSettings settings = new()
{
LicenseStore = GetLicenseStoreFolder(),
LoggerProvider = new NLogLoggerProvider()
};
Engine.Init(settings);
var dictionary = fixVersion.ToDictionary();
using (Session session = new("SslBuySide", "SslSellSide", dictionary))
{
session.Encryption = EncryptionMethod.Ssl;
var ssl = session.Ssl;
ssl.CertificateLocation = usePemCertificate ? "client.pem|client-pk.pem" : "client.p12";
if (verifyPeer || interactiveCertValidation)
{
ssl.VerifyPeer = true;
ssl.CaFile = usePemCertificate ? "cert-chain.pem" : "cert-chain.p12";
if(interactiveCertValidation)
{
ssl.RemoteCertificateValidationCallback = (object sender, in CertificateValidationArgs args) =>
{
Console.WriteLine("Server certificate: ");
Console.WriteLine(args.Certificate.ToString(false));
Console.Write("Pre-verification result: ");
if(args.VerificationResult)
{
Console.WriteLine("OK");
if(!AskForAcceptingCertificate(true))
{
args.ErrorDescription.Append("Rejected by custom callback");
args.VerificationResult = false;
}
}
else
{
Console.WriteLine("Failed!!!");
if(args.ErrorDescription.Length != 0)
{
Console.WriteLine("\nError description:\n" + args.ErrorDescription);
}
if (AskForAcceptingCertificate(false))
{
args.ErrorDescription.Clear();
args.VerificationResult = true;
}
}
};
}
}
ssl.PrivateKeyPassword = MakeSecureString("OnixS");
session.InboundApplicationMessage += (object sender, InboundMessageEventArgs e) =>
{
Console.WriteLine("Inbound application-level message:\n" + e.Message);
};
session.StateChanged += (object sender, SessionStateChangeEventArgs e) =>
{
Console.WriteLine("Session state: " + e.NewState);
};
session.Error += (object sender, SessionErrorEventArgs e) =>
{
Console.WriteLine("Error: " + e);
};
session.Warning += (object sender, SessionWarningEventArgs e) =>
{
Console.WriteLine("Warning: " + e);
};
session.LogonAsInitiator(IPAddress.Loopback, 10451, 30, true);
Message order = CreateOrderMessage(dictionary);
session.Send(order);
Console.WriteLine($"The order ({order}) was sent");
session.Logout("The session is disconnected by BuySide");
}
Engine.Shutdown();
}
/// <summary>
/// The main entry point for the application.
/// </summary>
private static int Main(string[] args)
{
try
{
var pemOption = new Option<bool>(
new[] { "--use-pem-certificate", "-p" },
"Option to use PEM certificate instead of default PFX one");
var verifyPeerOption = new Option<bool>(
new[] { "--verify-peer", "-v" },
"Specifies if the client should verify the server certificate");
var interactiveCertValidationOption = new Option<bool>(
new[] { "--interactive-validation", "-i" },
"The client may validate the server certificate in interactive mode");
var rootCommand = new RootCommand { pemOption, verifyPeerOption, interactiveCertValidationOption };
rootCommand.Description = "SSL Buy Side Sample";
rootCommand.TreatUnmatchedTokensAsErrors = true;
rootCommand.SetHandler<bool, bool, bool>(Run, pemOption, verifyPeerOption, interactiveCertValidationOption);
return rootCommand.Invoke(args);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return 1;
}
finally
{
// From https://github.com/NLog/NLog/wiki/Tutorial:
// NET Application running on Mono / Linux are required to stop threads / timers before entering application shutdown phase.
// Failing to do this will cause unhandled exceptions and segmentation faults, and other unpredictable behavior.
NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers
}
}
}
}