Category Archives: C#

Code Snippet: string split in c++ (c syntax)

Below code snippet useful while splitting string (char*) .

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

int split (const char *str, char c, char ***arr)
{
 int count = 1;
 int token_len = 1;
 int i = 0;
 char *p;
 char *t;

 p = str;
 while (*p != '\0')
 {
 if (*p == c)
 count++;
 p++;
 }

 *arr = (char**) malloc(sizeof(char*) * count);
 if (*arr == NULL)
 exit(1);

 p = str;
 while (*p != '\0')
 {
 if (*p == c)
 {
 (*arr)[i] = (char*) malloc( sizeof(char) * token_len );
 if ((*arr)[i] == NULL)
 exit(1);

 token_len = 0;
 i++;
 }
 p++;
 token_len++;
 }
 (*arr)[i] = (char*) malloc( sizeof(char) * token_len );
 if ((*arr)[i] == NULL)
 exit(1);

 i = 0;
 p = str;
 t = ((*arr)[i]);
 while (*p != '\0')
 {
 if (*p != c && *p != '\0')
 {
 *t = *p;
 t++;
 }
 else
 {
 *t = '\0';
 i++;
 t = ((*arr)[i]);
 }
 p++;
 }
 return count;
}

int main(void) {
 
 char* strToSplit ="REPLY id, login, cmd, volume, symbol, price, bid/ask, rate-id, status, message";
 int i;
 int c = 0;
 char **splittedarr = NULL;
 c = split(strToSplit, ',', ;&splittedarr)

 printf("found %d occurrence.\n", c);

 for (i = 0; i < c; i++)
 printf("string #%d:%s\n", i, splittedarr[i]);
 return 0;
}


Output
found 10 occurence.
string #0:REPLY id
string #1: login
string #2: cmd
string #3: volume
string #4: symbol
string #5: price
string #6: bid/ask
string #7: rate-id
string #8: status
string #9: message

How to place Order via FIX message?

Purpose

The purpose of this post is to walk-through implementation to “Place an Order on via FIX message channel”.

In my previous posts, I have shown basic implementation of FIX messages like Establish connection with broker/exchange Fix End points, Consume Market Feeds etc.

You can read my previous posts about topics mentioned above:

This post will cover following topics:

  • Different types of Orders.
  • Orders Validity
  • Order workflow.
  • Place Order
  • What is Execution Report?
  • Process Execution Report.

Order Types:

  • Market Order: This is basic type of order; wherein, trader buy or sell at market price without specifying desired buy or sell price.
  • Limit Order: It is an order to buy or sell at a specified price. A limit buy order can execute at specified buy price or lower. A limit sell order can execute at specified price or higher. In this order, trader has to specify price before placing an order.
  • Stop Order: It is similar to market order which execute when specific price triggers. For example, if the market price is 150, a trader places a buy stop order with a price of 60, when price would move 160 or above, this order will become market order and execute at best available price. This type order can be used for Take-Profit and Stop-Loss.
  • Stop Limit Order: This is a combination of stop order and limit order, like stop order, it is only processed if the market reaches a specific price, but it is executed limit order, therefore it will only get filled at the chosen or a better price. For example, if the current price is 150, a trader might place a buy stop limit order with a price of 160. If the market trades at 160 or above, this order will execute as limit order to get filled at 160. However, it might happen that this order may not fill if there is not depth available.

Detail description of each order types can be read on investopedia.

Order Validity

In addition to specify order types, trades can also specify validity of an order for how long particular order is valid. Order would be cancelled after expiry.

Traders can specify following validity of an order:

  • Day: Order is only valid till end of the market session.
  • GTC (Good till Cancel): Order is valid till trader manually cancels it. However, brokers might have max timeline to cancel orders automatically if order is beyond certain days, typically 30, 60 or 90 days.
  • GTD (Good till Date): Order is valid till end of the market session of specified date mention in this order.
  • IOC (Immediate or Cancel): Order should be partially/filled immediately while placing order; otherwise, it would be cancelled.
  • FOK (Fill or Kill): Either order would be fully filled or cancelled. This type of order would not allow any partial fills.

FIX Order workflow

OrderFlow

 

 

 

 

 

What is Execution Report?

It is FIX message which broker side sent in response to order request. Broker side relays status of an order, and there can be multiple Execution Reports for a single order. Execution reports can have following status and information:

Order Status

Order Status Description
Done for Day Order did not fully or partially filled; no further executions are pending for the trading day.
Filled Order completely filled; no remaining quantity.
Suspended Order has been placed in suspended state at the request of the client.
Canceled Canceled order with or without executions.
Expired Order has been canceled in broker’s system due to order validity (Time In Force) instructions.
Partially Filled Outstanding order with executions and remaining quantity.
Replaced Replaced order with or without executions
New Outstanding order with no executions
Rejected Order has been rejected by broker. NOTE: An order can be rejected subsequent to order acknowledgment, i.e. an order can pass from New to Rejected status.
Pending New Order has been received by brokers system but not yet accepted for execution. An execution message with this status will only be sent in response to a Status Request message.
Accepted Order has been received and is being evaluated for pricing.

Important Fields 

Field Description
ClOrderId Unique key of an order requested by client.
OrderId Unique key generated by broker/exchange for an order.
ExecID Unique key of each execution report message.
Account Account number on which order was placed.
OrdType Type of Order e.g. Market , Limit
Price Ordered Price specified to buy or sell
Side Side of an order (buy or Sell)
Symbol Symbol name of instrument on which order placed.
SecurityId InstrumentID
LastPx Orderd executed on this price.
LastQty Traded quantity in each fill
LeavesQty Remaining Qty of an order. It is zero when order is fully filled.
CumQty Total Traded Quantity

Implementation

Technology/Tools Used:

I have downloaded FIX UI Demo code from QuickFix/n Git hub location to save some time. This sample code already has connection and order routines. I will do further changes to show various use cases of order work flow with FIX 4.4 specification. I have also added FIXAcceptor component to process FIX messages locally.

Connect with FIX Acceptor

Click on connect button. Fix Initiator will send Logon message, which will be received by FIX acceptor and acknowledge it in reverse sending logon message.

Connect

Application is ready to place an order after connection is established.

Placing Order

Fix 4.4 supports “Single Order – New <D>” to place any single order by the client. You can find standard specification this message on any fix dictionary available online. However, message specification might differ from broker to broker.

You can see standard FIX messages/tags specification on FIXIMATE .

OrderTicket

 

 

 

 

 

 

 

 

Create an object of “NewOrderSingle” class and set values to properties of class:

Code:

// hard-coded fields
QuickFix.Fields.HandlInst fHandlInst = new QuickFix.Fields.HandlInst(QuickFix.Fields.HandlInst.AUTOMATED_EXECUTION_ORDER_PRIVATE);
// from params
QuickFix.Fields.OrdType fOrdType = FixEnumTranslator.ToField(orderType);
QuickFix.Fields.Side fSide = FixEnumTranslator.ToField(side);
QuickFix.Fields.Symbol fSymbol = new QuickFix.Fields.Symbol(symbol);
QuickFix.Fields.TransactTime fTransactTime = new QuickFix.Fields.TransactTime(DateTime.Now);
QuickFix.Fields.ClOrdID fClOrdID = GenerateClOrdID();
QuickFix.FIX44.NewOrderSingle nos = new QuickFix.FIX44.NewOrderSingle(fClOrdID, fSymbol, fSide, fTransactTime, fOrdType);
nos.HandlInst = fHandlInst;
nos.OrderQty = new QuickFix.Fields.OrderQty(orderQty);
nos.TimeInForce = FixEnumTranslator.ToField(tif);
if (orderType == OrderType.Limit)
    nos.Price = new QuickFix.Fields.Price(price);

Process Execution Report

ExecutionReport

 

 

 

 

public void HandleExecutionReport(QuickFix.FIX44.ExecutionReport msg)
        {
        string execId = msg.ExecID.Obj;
        string execType = FixEnumTranslator.Translate(msg.ExecType);                Trace.WriteLine("EVM: Handling ExecutionReport: " + execId + " / " + execType);
       ExecutionRecord exRec = new  ExecutionRecord(                                msg.ExecID.Obj,                    
msg.OrderID.Obj,                    
string.Empty,
execType,
msg.Symbol.Obj,                   
FixEnumTranslator.Translate(msg.Side));

exRec.LeavesQty = msg.LeavesQty.getValue();        
exRec.TotalFilledQty = msg.CumQty.getValue();                exRec.LastQty = msg.LastQty.getValue();
       }

FIX Acceptor

This is server side component which process messages from FIX clients and send response back to them.

Executor Class

This class is getting various methods callbacks once received FIX message from FIX Client.

public void OnMessage(QuickFix.FIX44.NewOrderSingle n, SessionID s)

This method will be called every time when “NewOrderSingle” message received.

I am simulating different status of an execution report. I have added 1 sec sleep time between each status change and can be clearly seen in UI.

public void OnMessage(QuickFix.FIX44.NewOrderSingle n, SessionID s)
 {
 Symbol symbol = n.Symbol;
 Side side = n.Side;
 OrdType ordType = n.OrdType;
 OrderQty orderQty = n.OrderQty;
 Price price = new Price(DEFAULT_MARKET_PRICE);
 ClOrdID clOrdID = n.ClOrdID;
 
 switch (ordType.getValue())
 {
 case OrdType.LIMIT:
 price = n.Price;
 if (price.Obj == 0)
 throw new IncorrectTagValue(price.Tag);
 break;
 case OrdType.MARKET: break;
 default: throw new IncorrectTagValue(ordType.Tag);
 }

// Send Status New
 SendExecution(s, OrdStatus.NEW, ExecType.NEW, n, n.OrderQty.getValue(), 0, 0, 0, 0);
 Thread.Sleep(1000);
 
 // Send Status Partially Filled
 decimal filledQty = Math.Abs(Math.Round(n.OrderQty.getValue() / 4, 2));
 decimal cumQty = filledQty;
 SendExecution(s, OrdStatus.PARTIALLY_FILLED, ExecType.PARTIAL_FILL, n, filledQty, filledQty, price.getValue(), filledQty, price.getValue());
 Thread.Sleep(1000);

// Send Status Partially Filled
 filledQty = Math.Abs(Math.Round(n.OrderQty.getValue() / 4, 2));
 cumQty += filledQty;
 SendExecution(s, OrdStatus.PARTIALLY_FILLED, ExecType.PARTIAL_FILL, n, n.OrderQty.getValue() - cumQty, cumQty, price.getValue(), filledQty, price.getValue());
Thread.Sleep(1000);

// Send Status Fully Filled
 filledQty = n.OrderQty.getValue() - cumQty;
 cumQty += filledQty;
 SendExecution(s, OrdStatus.FILLED, ExecType.FILL, n, 0, cumQty, price.getValue(), filledQty, price.getValue());
 }

private void SendExecution(SessionID s, char ordStatus, char execType, QuickFix.FIX44.NewOrderSingle n, decimal leavesQty, decimal cumQty, decimal avgPx, decimal lastQty, decimal lastPrice)
 {

 QuickFix.FIX44.ExecutionReport exReport = new QuickFix.FIX44.ExecutionReport(
 new OrderID(GenOrderID()),
 new ExecID(GenExecID()),
 new ExecType(execType),
 new OrdStatus(ordStatus),
 n.Symbol, //shouldn't be here?
 n.Side,
 new LeavesQty(leavesQty),
 new CumQty(cumQty),
 new AvgPx(avgPx));

 exReport.ClOrdID = new ClOrdID(n.ClOrdID.getValue());
 exReport.Set(new LastQty(lastQty));
 exReport.Set(new LastPx(lastPrice));

 if (n.IsSetAccount())
 exReport.SetField(n.Account);

 try
 {
 Session.SendToTarget(exReport, s);
 }
 catch (SessionNotFound ex)
 {
 Console.WriteLine("==session not found exception!==");
 Console.WriteLine(ex.ToString());
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.ToString());
 }
 }

This post exhibits standard way of handling FIX messages; however, implementation can vary from broker to broker.

I hope, this post will give you good understanding of how order can be placed via FIX channel. I will cover order cancel and replace scenario in next post.

Source Code

Source code can be downloaded from github repository. Executable files are placed in separate folder.

 

Default Application Domains in CLR

 
When CLR loads first to execute managed code, it creates 3 application domains.  System and Shared domains are singleton for each CLR.

 

Application Domains

 

System Domain

    • It creates and Shared and default application Domains.
    • It Loads Mscorlib.dll (class library for system namespace) into shared domain.
    • Creation of default Exception instances (OutOfMemoryException, StackOverFlowException and FatalExecutionException): the reason to create these exceptions in advance is that in case of above exceptions to be raise system needs to creates instance and memory, but say if it is outofmemory exception then there will be no space to instantiate exception instance.
    • It keeps track all the domains in the process and manage them by loading or unloading AppDomains.

    Shared Domain

     

    • It contains mscorlib.dll
    • It contains basic system types like class, array, struct etc.

    Default Domain

     

  • It contains user code and resources for only this app domain.
  • Name of default domain is name of executable.

 

 

Reference: http://msdn.microsoft.com/en-us/magazine/cc163791.aspx#S1

Setup connection on SSL with FIX server

 

Some broker support SSL to send message to FIX server. QuickFix engine does not have support for SSL. Some commercial QuickFix engine support it eg. OnixS ultra low latency .NET FIX Engine.

 

Here I am give example how we can connect to FIX server on SSL.

We can install third party network proxy software which can route calls on SSL. We can  Stunnel program to support it. It can be used to add SSL functionality to commonly used in daemons like POP2, POP3, and IMAP servers without any changes in the programs’ code.

QuickFIX Configuration in Stunnel

Add following entry to Stunel.conf config file to connect with FIX Server

;it can be any name

[FIXSERVER]
client = yes
accept = 127.0.0.1:3500
connect=<FIXSERVER Address>: <PORT>

 

image

 

You can change listening port to any free port on local machine.

 

Change in QuickFix config File

[default]
PersistMessages=Y
ConnectionType=initiator
UseDataDictionary=Y

[SESSION]
ConnectionType=initiator
FileStorePath=store
FileLogPath=fixlog
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0

TransportDataDictionary=FIXT.1.1.xml

AppDataDictionary=FIX50.xml
SenderCompID=ABC
TargetCompID=FIXSERVER
SocketConnectHost=127.0.0.1
SocketConnectPort=3500
HeartBtInt=20
ReconnectInterval=30
ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y

 

image

 

I hope, it will help you.

Implementation of FIX messages for Fix 5.0 sp2 and FIXT1.1 specification

 

This post will demonstrate how to connect with FIX5.0 server and FIXT1.1 specification and uses of QuickFix/n (native .net FIX engine).

Introduction

With this release of FIX protocol version 5.0, a new Transport Independence framework (TI) introduced which separates the FIX Session Protocol from the FIX Application Protocol. This gives freedom to send message across different messaging technologies like MSMQ, message bus etc.

Because of different versions of transport and application protocol, we will have to explicitly defines settings in config file.

TransportDataDictionary is used for defining transport protocol version eg. FIXT.1.1.xml

AppDataDictionary is used for defining data dictionary for FIX application protocol version eg. FIX50.xml

You can read more about FIXT1.1 and FIX 5.0 Sp2 specification on fixprotocol.org.

http://fixprotocol.org/specifications/FIXT.1.1

http://fixprotocol.org/specifications/FIX.5.0

 

QuickFix/N

To demonstrate implementation of FIX 5.0 sp2, I’ll use open source FIX engine for .net (QuickFix/N) which is one of open source engine in native .net code.  Code for quickfix.net is available on github and primarily contributed by Connamara System’s developers. These guys are doing commendable job.

 

Implementation

FixInitiator

This is client application which will connect with FIX server to send and receive FIX messages. I am demonstrating implementation of MarketDataRequest and its responses (MarketDataSnapshot & MarketDataIncrementalRefresh).

 

Start with Configuration

First we create configuration file for initiator.

[default]
PersistMessages=Y
ConnectionType=initiator
UseDataDictionary=Y

[SESSION]
ConnectionType=initiator
FileStorePath=store
FileLogPath=fixlog
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0

TransportDataDictionary=FIXT.1.1.xml

AppDataDictionary=FIX50.xml
SenderCompID=ABC
TargetCompID=FIXSERVER
SocketConnectHost=127.0.0.1
SocketConnectPort=3500
HeartBtInt=20
ReconnectInterval=30
ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y

 

*Note- AppDataDictionary is for application protocol eg. FIX 5.0 and TransportDataDictionary is for transport protocol.

You can read more about configuration here.

Create Application Class

Before starting with implementation you would need to have quickFix.dll which is available of github  athttps://github.com/connamara/quickfixn

 

To connect with FIX session, you will have to implement QuickFix.Application interface.

public interface Application
{
void FromAdmin(Message message, SessionID sessionID);
void FromApp(Message message, SessionID sessionID);
void OnCreate(SessionID sessionID);
void OnLogon(SessionID sessionID);
void OnLogout(SessionID sessionID);
void ToAdmin(Message message, SessionID sessionID);
void ToApp(Message message, SessionID sessionId);
}

I create one class named FixClient50Sp2 which implements interface and inherit base class for message cracking and getting message events.

image

 

FIX Application Setup

 

Setting up Initiator

// FIX app settings and related
var settings = new SessionSettings(“C:\initiator.cfg”);

// FIX application setup
MessageStoreFactory storeFactory = new FileStoreFactory(settings);
LogFactory logFactory = new FileLogFactory(settings);
_client = new FixClient50Sp2(settings);

IInitiator initiator = new SocketInitiator(_client, storeFactory, settings, logFactory);
_client.Initiator = initiator;

 

* _client is instance of class FixClient50Sp2.

Starting Initiator

_client.Start();

 

Implementation of QuickFix.Application Interface methods

/// <summary>
/// every inbound admin level message will pass through this method,
/// such as heartbeats, logons, and logouts.
/// </summary>
/// <param name=”message”></param>
/// <param name=”sessionId”></param>
public void FromAdmin(Message message, SessionID sessionId)
{
Log(message.ToString());
}

/// <summary>
/// every inbound application level message will pass through this method,
/// such as orders, executions, secutiry definitions, and market data.
/// </summary>
/// <param name=”message”></param>
/// <param name=”sessionID”></param>
public void FromApp(Message message, SessionID sessionID)
{
Trace.WriteLine(“## FromApp: ” + message);

Crack(message, sessionID);
}

/// <summary>
/// this method is called whenever a new session is created.
/// </summary>
/// <param name=”sessionID”></param>
public void OnCreate(SessionID sessionID)
{
if (OnProgress != null)
Log(string.Format(“Session {0} created”, sessionID));
}

/// <summary>
/// notifies when a successful logon has completed.
/// </summary>
/// <param name=”sessionID”></param>
public void OnLogon(SessionID sessionID)
{
ActiveSessionId = sessionID;
Trace.WriteLine(String.Format(“==OnLogon: {0}==”, ActiveSessionId));

if (LogonEvent != null)
LogonEvent();
}

/// <summary>
/// notifies when a session is offline – either from
/// an exchange of logout messages or network connectivity loss.
/// </summary>
/// <param name=”sessionID”></param>
public void OnLogout(SessionID sessionID)
{
// not sure how ActiveSessionID could ever be null, but it happened.
string a = (ActiveSessionId == null) ? “null” : ActiveSessionId.ToString();
Trace.WriteLine(String.Format(“==OnLogout: {0}==”, a));

if (LogoutEvent != null)
LogoutEvent();
}

/// <summary>
/// all outbound admin level messages pass through this callback.
/// </summary>
/// <param name=”message”></param>
/// <param name=”sessionID”></param>
public void ToAdmin(Message message, SessionID sessionID)
{
Log(“To Admin : ” + message);
}

/// <summary>
/// all outbound application level messages pass through this callback before they are sent.
/// If a tag needs to be added to every outgoing message, this is a good place to do that.
/// </summary>
/// <param name=”message”></param>
/// <param name=”sessionId”></param>
public void ToApp(Message message, SessionID sessionId)
{
Log(“To App : ” + message);
}

 

 

Callback to Subscription

public void OnMessage(MarketDataIncrementalRefresh message, SessionID session)
{
var noMdEntries = message.NoMDEntries;
var listOfMdEntries = noMdEntries.getValue();
//message.GetGroup(1, noMdEntries);
var group = new MarketDataIncrementalRefresh.NoMDEntriesGroup();

Group gr = message.GetGroup(1, group);

string sym = message.MDReqID.getValue();

var price = new MarketPrice();

for (int i = 1; i <= listOfMdEntries; i++)
{
group = (MarketDataIncrementalRefresh.NoMDEntriesGroup)message.GetGroup(i, group);

price.Symbol = group.Symbol.getValue();

MDEntryType mdentrytype = group.MDEntryType;

if (mdentrytype.getValue() == ‘0’) //bid
{
decimal px = group.MDEntryPx.getValue();
price.Bid = px;
}
else if (mdentrytype.getValue() == ‘1’) //offer
{
decimal px = group.MDEntryPx.getValue();
price.Offer = px;
}

price.Date = Constants.AdjustedCurrentUTCDate.ToString(“yyyyMMdd”);
price.Time = group.MDEntryTime.ToString();
}

if (OnMarketDataIncrementalRefresh != null)
{
OnMarketDataIncrementalRefresh(price);
}
}

 

Code can be found at github.

 

https://github.com/neerajkaushik123/Fix50Sp2SampleApp.git

Arithmetic Expression Calculator

 

This post describes implementation of arithmetic expression calculator in C#.

I have below specification in the form of Backus–Naur Form expression. It is mainly used to define syntax of languages in compiler design world. You can read more about it on wikipedia.

There are certain readymade solutions are available to calculate expressions.  Microsoft also introduced Expression tree through which we can compile complex expressions using Lambda expressions

Basically this expression is defining arithmetic calculation with some validations. Following defines list of validations and rule.

  • Expression should contain arithmetic operations and operands only.
  • Expression can only have +, -, * operators.
  • Numbers can be negative or positive.
  • Decimals numbers can have “.” as decimal.
  • Digits should be non zero.
    Expression Example

cmd::= expression* signed_decimal

expresion::= signed_decimal ‘ ‘* operator ‘ ‘* eg. 2.3 * + * 2.3

operator::= ‘+’ | ‘-‘ | ‘*’

signed_decimal::= ‘-‘? decimal_number

decimal_number::= digits | digits ‘.’ digits

digits::= ‘0’ | non_zero_digit digits*

non_zero_digit::= ‘1’|’2’|’3’|’4’|’5’|’6’|’7’|’8’|’9′

Let’s start Implementation

Here I’ll take simple approach to evaluate this expression without use of .Net Expression Tree with possible failure and pass test cases. I tried to put relevant comments in code itself to make it self explanatory.

 

using System;
using System.Linq;
using NUnit.Framework;

namespace Calculator
{
    /// 
    /// Basic calc engine for handling +, -, * operations.
    /// 
    public class CalcEngine
    {

        char[] operatorList = new char[3] { '+', '-', '*' };

        /// 
        /// Calculate arithmatic Expression
        /// 
        /// 
        /// 
        public decimal Calculate(string expression)
        {
            foreach (var oper in operatorList)
            {
                if (expression.Contains(oper))
                {
                    var parts = expression.Split(oper);
                    int i = 1;
                    decimal result = Calculate(parts[0].Trim());

                    while (i < parts.Length)
                    {
                        switch (oper)
                        {
                            case '+':
                                result += Calculate(parts[i].Trim());
                                break;
                            case '-':
                                result -= Calculate(parts[i].Trim());
                                break;
                            case '*':
                                result *= Calculate(parts[i].Trim());
                                break;
                        }
                        i++;
                    }
                    return result;
                }

            }
            decimal value = 0;
   //Note: we can also use decimal.Parse and can catch exception in catch block 
   but it is expensive task to wait for system exception
   //better to use TryParse and then throw custom exception
   if (expression.Trim().Length > 0 && 
         !decimal.TryParse(expression, System.Globalization.NumberStyles.Float, 
	  System.Globalization.CultureInfo.InvariantCulture, out value))
            {
                throw new FormatException("Expression is wrong! 
			Please removed un-allowed charactersn 
			please follow following validations:n" +
                    "Expression should contain arithmetic operations
			 and operands only n" +
                    "Expression can only have +, -, * operators n" +
                    "Numbers can be negative or positive n" +
                    ". as decimal point");
            }

            return value;
        }

    }

Unit Tests

[TestFixture]
    public class CalcEngineTest
    {
        CalcEngine engine;

        [Setup]
        public void Setup()
        {
            engine = new CalcEngine();
        }

        [Test]
        [ExpectedException(typeof(FormatException))]
        public void TestValidationFailur_Nondigit()
        {
            Assert.AreEqual(-14, engine.Calculate("1+2+3-4d"));
        }

        [Test]
        [ExpectedException(typeof(FormatException))]
        public void TestValidationFailure_NonDecimalNotation()
        {
            Assert.AreEqual(0, engine.Calculate("1+2+3,4"));
        }

        [Test]
        public void TestBlankStringShouldZero()
        {
            Assert.AreEqual(0, engine.Calculate(" "));


        }


        [Test]
        public void TestMultiplication()
        {
            Assert.AreEqual(60, engine.Calculate("5*6*2"));

            Assert.AreEqual(469.929812m, engine.Calculate("5.5*2.456*34.789"));
        }

        [Test]
        public void TestSubtraction()
        {

            Assert.AreEqual(45, engine.Calculate("100-35-20"));

            Assert.AreEqual(469.929812m, engine.Calculate("5.5*2.456*34.789"));
        }


    [Test]
    public void TestSummation()
    {
    CalcEngine engine = new CalcEngine();
    Assert.AreEqual(10, engine.Calculate("1+2+3+4"));
    Assert.AreEqual(20.20m, engine.Calculate("1.4+4.5+8.90+5.4"));
    Assert.AreEqual(20 + 20, engine.Calculate("20+20"));
    }

 [Test]
 public void TestAdd_Multiply_Subtraction()
 {
  Assert.AreEqual(-5563.4541m, 
		engine.Calculate("-2*4.5+3+30-20*278.9+30.5459-40"));
  Assert.AreEqual(-5563.4541m, 
		engine.Calculate("-2*4.5+3+30-20*278.9+30.5459-40"));
 }
    [Test]
    public void TestDecimalNumberWithoutOperators()
    {
       Assert.AreEqual(4.5m, engine.Calculate("4.5"));
     }
  }
}