Transaction
Namespace: WealthLab.Backtest
Parent: Object

The Transaction class represents the result of an order (buy, sell, sell short, or cover short) that was placed either during a simulated backtest, or in the WealthLab Order Manager.

When developing a C# Coded Strategy, you obtain a Transaction instance as a result of the PlaceTrade method. You can modify certain properties of the Transaction, such as Quantity, to implement customized position sizing in your trading Strategy.

Backtest Related
Commission
public double Commission

The commission that was applied to the order. In WealthLab, you establish commission in the Backtest Settings interface.


EntryDate
public DateTime EntryDate

The signal entry date of the transaction. Corresponds to the price bar on which the trading signal was issued.


ExecutionDate
public DateTime ExecutionDate

The date that the backtester filled the order during the simulation.


ExecutionPrice
public double ExecutionPrice

The price at which the backtester filled the order during the simulation.


MarginOverride
public bool MarginOverride

Set this property to true to cause the backtester to fill the transaction even if there is not enough simulated capital.


PositionTag
public int PositionTag

This integer value is used internally by WealthLab to track which Building Block a particular Transaction belongs to. Avoid using this yourself. Instead, you can use the Tag property to store information in a Transaction.


Weight
public double Weight

Specifies a weight value to use for determining which transactions the backtester should fill in cases where there are more candidate transactions than available simulated capital. Transactions are sorted by this value during the backtest process, so higher values here will result in a higher priority.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;

namespace WealthScript2
{
	public class MyStrategy : UserStrategyBase
	{       
		public override void Initialize(BarHistory bars)
		{
			_sma1 = SMA.Series(bars.Close, 8);
			_sma2 = SMA.Series(bars.Close, 20);
			_rsi = RSI.Series(bars.Close, 10);

			PlotIndicator(_sma1);
			PlotIndicator(_sma2);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{				
				Transaction t = null;
				if (_sma1.CrossesOver(_sma2, idx))
					t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market);

				// Assign the highest priority to the lowest rsi (most oversold) by negation
				if (t != null)
					t.Weight = -_rsi[idx];
			}
			else
			{
				if (_sma1.CrossesUnder(_sma2, idx))
					PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}
		}

		//private variables
		SMA _sma1;
		SMA _sma2;
		RSI _rsi;
	}
}


Members
AutoProfitTargetPrice
public double AutoProfitTargetPrice

Assigning a value to this property allows WL7 to issue an immediate "same-bar" limit order to exit the resulting Position as soon as it is filled. This occurs both in backtesting and in live trading.

In backtesting, the backtester will attempt to sell the resulting Position at the limit price on the same bar it was entered. For real-world accuracy, you should use this option with market entry orders only. If you apply same-bar exits to limit/stop entries, the backtester will still execute them, but actually there is some uncertainty as to whether the same-bar exit could have really been filled, depending in how the price data moved during the entry bar. In a future release, we will expose same-bar processing to our Granular Processing feature for more realistic results in backtesting.

In live trading, WL7 will issue the limit order as soon as it detects the Transaction is filled in the Order Manager. Note: some brokers might not support issuing two sell orders for a single position, or they might support this only through use of a One-Cancels-Other order type (see Use OCO in Data/Trading Preferences).

Contrast this example that assigns the AutoProfitTargetPrice based on the last close (basis price) with that of AssignAutoStopTargetPrices() in UserStrategyBase. The latter allows you to use the actual execution price as a basis for the same-bar exit orders.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript5
{
	public class SameBarAuto : UserStrategyBase
	{
		TimeSeries _band;

		public override void Initialize(BarHistory bars)
		{
			double pct = 0.95;
			_band = SMA.Series(bars.Close, 10) * pct;
			PlotTimeSeriesLine(_band, "band", "Price");
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (HasOpenPosition(bars, PositionType.Long))
			{
				//sell at open next bar
				PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}
			else
			{
				//buy at market if the low goes below the band, but closes above it
				if (bars.Low.CrossesUnder(_band[idx], idx) && bars.Close[idx] > _band[idx])
				{
					Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market);
					t.AutoProfitTargetPrice = bars.Close[idx] * 1.03;
					t.AutoStopLossPrice = bars.Close[idx] * 0.97;
				}
			}
		}
	}
}

AutoStopLossPrice
public double AutoStopLossPrice

Assigning a value to this property allows WL7 to issue an immediate "same-bar" stop order to exit the resulting Position as soon as it is filled. This occurs both in backtesting and in live trading.

In backtesting, the backtester will attempt to sell the resulting Position at the stop price on the same bar it was entered. For real-world accuracy, you should use this option with market entry orders only. If you apply same-bar exits to limit/stop entries, the backtester will still execute them, but actually there is some uncertainty as to whether the same-bar exit could have really been filled, depending in how the price data moved during the entry bar. In a future release, we will expose same-bar processing to our Granular Processing feature for more realistic results in backtesting.

In live trading, WL7 will issue the stop order as soon as it detects the Transaction is filled in the Order Manager. Note: some brokers might not support issuing two sell orders for a single position, or they might support this only through use of a One-Cancels-Other order type (see Use OCO in Data/Trading Preferences).

Contrast this example that assigns the AutoStopLossPrice based on the last close (basis price) with that of AssignAutoStopTargetPrices() in UserStrategyBase. The latter allows you to use the actual execution price as a basis for the same-bar exit orders.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript5
{
	public class SameBarAuto : UserStrategyBase
	{
		TimeSeries _band;

		public override void Initialize(BarHistory bars)
		{
			double pct = 0.95;
			_band = SMA.Series(bars.Close, 10) * pct;
			PlotTimeSeriesLine(_band, "band", "Price");
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (HasOpenPosition(bars, PositionType.Long))
			{
				//sell at open next bar
				PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}
			else
			{
				//buy at market if the low goes below the band, but closes above it
				if (bars.Low.CrossesUnder(_band[idx], idx) && bars.Close[idx] > _band[idx])
				{
					Transaction t = PlaceTrade(bars, TransactionType.Buy, OrderType.Market);
					t.AutoProfitTargetPrice = bars.Close[idx] * 1.03;
					t.AutoStopLossPrice = bars.Close[idx] * 0.97;
				}
			}
		}
	}
}

Bars
public BarHistory Bars

The BarHistory instance that the transaction was placed on.


GranularWeightBasis
public DateTime GranularWeightBasis

If the Advanved Strategy Setting "Use Granular Limit/Stop Processing" is enabled, this property will contain the DateTime that was detected in the intraday day data that determined when the limit/stop order was executed.


IsEntry
public bool IsEntry

Returns true if this is an entry (Buy or Short) transaction.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript7
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _sma;

		public override void Initialize(BarHistory bars)
		{
			_sma = SMA.Series(bars.Close, 10);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				if (bars.Close.CrossesOver(_sma, idx))
					PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0, "Xover");
				else
					PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Low[idx] * 0.98, "Dip buy");
			}
			else
			{
				Position p = LastPosition;
				if (p.EntrySignalName.ToUpper() == "XOVER")
				{
					//sell this one after 5 bars
					if (idx + 1 - p.EntryBar >= 5)
						PlaceTrade(bars, TransactionType.Sell, OrderType.Market);

				}
				else
				{
					//sell the dip buy the next day
					PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
				}			
			}	        
		}

		//write the Signals to the Debug Log - first the exits, then entries
		public override void BacktestComplete()
		{
			WriteToDebugLog("*** Exit Signals ***");
			foreach (Transaction t in Backtester.Orders)
				if (t.IsExit)
					OrderFormat(t);

			WriteToDebugLog("\r\n*** Entry Signals ***");
			foreach (Transaction t in Backtester.Orders)
				if (t.IsEntry)
					OrderFormat(t);
		}

		private void OrderFormat(Transaction t)
		{
			string s = String.Format("{0,-6}{1,6} {2,-6}{3,8}",
				t.TransactionType, t.Quantity, t.Symbol, t.OrderType);

			if (t.OrderType == OrderType.Limit || t.OrderType == OrderType.Stop)
				s += (" @ " + t.OrderPriceDisplay);

			WriteToDebugLog(s);
		}
	}
}

IsExit
public bool IsExit

Returns true if this is an exit (Sell or Cover) transaction.

** Example **

  • See Is Entry

NSF
public bool NSF

Returns true if the Transaction was flagged as Non-Sufficient Funds by the backtester. These Transactions are still processed an tracked on a per symbol basis, in order to preserve the integrity of the Strategy. You can disable this behavior by turning off the Retain NSF Positions in the Advanced Strategy Settings of the Strategy Window.


OrderPrice
public double OrderPrice

The order price of the transaction. Does not apply for Market orders.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript123
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _sma;

		public override void Initialize(BarHistory bars)
		{
			_sma = SMA.Series(bars.Close, 10);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				if (bars.Close.CrossesOver(_sma, idx))
					PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
			}
			else
			{
				PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}	        
		}

		//write the Signals to the Debug Log
		public override void BacktestComplete()
		{
			foreach (Transaction t in Backtester.Orders)
			{
				string s = String.Format("{0,-6}{1,6} {2,-6}{3,8}",
					t.TransactionType, t.Quantity, t.Symbol, t.OrderType);

				if (t.OrderType == OrderType.Limit || t.OrderType == OrderType.Stop)
					s += (" @ " + t.OrderPriceDisplay);

				WriteToDebugLog(s);
			}
		}
	}
}

OrderType
public OrderType OrderType

Contains the order type of the transaction. Possible values are OrderType.Market, OrderType.Limit, OrderType.Stop and OrderType.FixedPrice. Market, Limit, and Stop simulate their corresponding broker order types, while FixedPrice is a theoretical order type intended to support situations where you want to establish a fixed price of an order.


PositionType
public PositionType PositionType

Returns the position type of the transaction, PositionType.Long for Buy and Sell orders, and PositionType.Short for Short and Cover orders.


Quantity
public double Quantity

Contains the number of shares or contracts in the transaction.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript123
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _sma;

		public override void Initialize(BarHistory bars)
		{
			_sma = SMA.Series(bars.Close, 10);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				if (bars.Close.CrossesOver(_sma, idx))
					PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
			}
			else
			{
				PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}	        
		}

		//write the Signals to the Debug Log
		public override void BacktestComplete()
		{
			foreach (Transaction t in Backtester.Orders)
			{
				string s = String.Format("{0,-6}{1,6} {2,-6}{3,8}",
					t.TransactionType, t.Quantity, t.Symbol, t.OrderType);

				if (t.OrderType == OrderType.Limit || t.OrderType == OrderType.Stop)
					s += (" @ " + t.OrderPriceDisplay);

				WriteToDebugLog(s);
			}
		}
	}
}

SignalName
public string SignalName

Allows you to save an optional signal name string along with the Transaction. This eventually gets passed to the EntrySignalName property of the resulting Position object.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript7
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _sma;

		public override void Initialize(BarHistory bars)
		{
			_sma = SMA.Series(bars.Close, 10);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (OpenPositions.Count == 0)
			{
				if (bars.Close.CrossesOver(_sma, idx))
					PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0, "Xover");
				else
					PlaceTrade(bars, TransactionType.Short, OrderType.Limit, bars.High[idx] * 1.02, "Take a Shot");
			}
			else
			{
				Position p = LastPosition;
				if (p.EntrySignalName.ToUpper() == "XOVER")
				{
					//sell this one after 5 bars
					if (idx + 1 - p.EntryBar >= 5)
						PlaceTrade(bars, TransactionType.Sell, OrderType.Market);

				}
				else
				{
					//cover at the previous day close, or 3% stop loss					
					PlaceTrade(bars, TransactionType.Cover, OrderType.Stop, p.EntryPrice * 1.03, "Stop Loss");
					PlaceTrade(bars, TransactionType.Cover, OrderType.Limit, bars.Close[idx], "Get Out Quick");
				}			
			}	        
		}

		//write the Signals to the Debug Log - first the exits, then entries
		public override void BacktestComplete()
		{
			WriteToDebugLog("*** Exit Signals ***");
			foreach (Transaction t in Backtester.Orders)
				if (t.IsExit)
					OrderFormat(t);

			WriteToDebugLog("\r\n*** Entry Signals ***");
			foreach (Transaction t in Backtester.Orders)
				if (t.IsEntry)
					OrderFormat(t);
		}

		private void OrderFormat(Transaction t)
		{
			string s = String.Format("{0,-6}{1,6} {2,-6}{3,8}",
				t.TransactionType, t.Quantity, t.Symbol, t.OrderType);

			if (t.OrderType == OrderType.Limit || t.OrderType == OrderType.Stop)
				s += (" @ " + t.OrderPriceDisplay);
			else
				s += (new String(' ', 10));

			s += ("\t" + t.SignalName);

			WriteToDebugLog(s);
		}
	}
}

Symbol
public string Symbol

The symbol that the transaction was placed on.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript123
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _sma;

		public override void Initialize(BarHistory bars)
		{
			_sma = SMA.Series(bars.Close, 10);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				if (bars.Close.CrossesOver(_sma, idx))
					PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
			}
			else
			{
				PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}	        
		}

		//write the Signals to the Debug Log
		public override void BacktestComplete()
		{
			foreach (Transaction t in Backtester.Orders)
			{
				string s = String.Format("{0,-6}{1,6} {2,-6}{3,8}",
					t.TransactionType, t.Quantity, t.Symbol, t.OrderType);

				if (t.OrderType == OrderType.Limit || t.OrderType == OrderType.Stop)
					s += (" @ " + t.OrderPriceDisplay);

				WriteToDebugLog(s);
			}
		}
	}
}

Tag
public object Tag

If you assign a value to the Tag property of a Transaction instance that opened a new position (a Buy or Short TransactionTypes), that value will be assigned to the Tag property of the Position instance that is created.


TransactionType
public TransactionType TransactionType

Contains the type of transaction, possible values are TransactionType.Buy, TransactionType.Sell, TransactionType.Short or TransactionType.Cover.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript123
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _sma;

		public override void Initialize(BarHistory bars)
		{
			_sma = SMA.Series(bars.Close, 10);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				if (bars.Close.CrossesOver(_sma, idx))
					PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, bars.Close[idx]);
			}
			else
			{
				PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}	        
		}

		//write the Signals to the Debug Log
		public override void BacktestComplete()
		{
			foreach (Transaction t in Backtester.Orders)
			{
				string s = String.Format("{0,-6}{1,6} {2,-6}{3,8}",
					t.TransactionType, t.Quantity, t.Symbol, t.OrderType);

				if (t.OrderType == OrderType.Limit || t.OrderType == OrderType.Stop)
					s += (" @ " + t.OrderPriceDisplay);

				WriteToDebugLog(s);
			}
		}
	}
}