I have this base class MartingaleBase which I found in the forum but having difficulty implementing it into my strategy. I've inherited the class in my strategy like below, created a variable for it but having a problem calling the class in the onstatechange and the on bar update method. I'm not sure how to do this. Basically I've passed the base class method getQty() as a parameter into the enter long method, but when I enable the strategy it does not load.
Just some background to the class, it basically allows you to double down when you have a loosing trade. For example start with x quantity, if you have a winning trade keep x quantity the same. If you have a losing trade, the next trade will be 2x quantity, if you have another losing trade the next trade will be 4x and so on until you win and recover losses and the multiplier resets to 1 x again.
See below snippet of the MartingaleBase code. Any help would be appreciated.
//This namespace holds Strategies in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Strategies
{
abstract public class MartingaleBase : Strategy
{
#region Variables
// Double up on losing trades
public enum eTradeResult { WINNER, LOSER, BREAK_EVEN };
public enum MaxDoubleUp { One, Two, Four, Eight };
protected List<eTradeResult> listWinLosses; // Each element indicates whether a trade finished a winner or a loser.
private List<double> dailyListProfits; // Each element indicates the profit of a trade
// The following are used to calculate daily (runtime profit/loss) for the current trade
private Queue<double> entryPrices;
private Queue<double> exitPrices;
#endregion
[Browsable(false)]
public double Commission
{
get; set;
}
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"The purpose of this class is to serve as a base class to easily implement the Maritngale entry strategy to any strategy.";
Name = "MartingaleBase";
Calculate = Calculate.OnBarClose;
EntriesPerDirection = 1;
EntryHandling = EntryHandling.AllEntries;
IsExitOnSessionCloseStrategy = true;
ExitOnSessionCloseSeconds = 30;
IsFillLimitOnTouch = false;
MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
OrderFillResolution = OrderFillResolution.Standard;
Slippage = 0;
StartBehavior = StartBehavior.WaitUntilFlat;
TimeInForce = TimeInForce.Gtc;
TraceOrders = false;
RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
StopTargetHandling = StopTargetHandling.PerEntryExecution;
BarsRequiredToTrade = 20;
// Disable this property for performance gains in Strategy Analyzer optimizations
// See the Help Guide for additional information
IsInstantiatedOnEachOptimizationIteration = false;
UseMartingaleQty = true;
MaxExpValue = MaxDoubleUp.Eight;
ShowStrategyPost = true;
ShowStrategyPostLocation = TextPosition.TopLeft;
PLBoxColorGains = Brushes.Blue;
PLBoxColorLosses = Brushes.Red;
PLBoxTextColorGains = Brushes.White;
PLBoxTextColorLosses = Brushes.Black;
PLBoxOpacity = 20;
}
else if (State == State.Configure)
{
listWinLosses = new List<eTradeResult>();
dailyListProfits = new List<double>();
entryPrices = new Queue<double>();
exitPrices = new Queue<double>();
}
}
protected override void OnExecutionUpdate( Cbi.Execution execution, string executionId, double price, int quantity,
Cbi.MarketPosition marketPosition, string orderId, DateTime time)
{
Trade lastTrade = null;
try
{
#region Strategy post and session array management
try
{
if (execution.IsExitStrategy)
{
if( execution.Order.OrderState == OrderState.Filled
&& execution.Order.Filled > 0 )
{
// First, set the exit object
for (int i = 0; i < execution.Quantity; ++i)
{
exitPrices.Enqueue(execution.Price);
}
// Note: the reason why it seems like we passing in the opposite direction that you'd expect is because of my design...
// At this point in the vector I no longer have access to the appropriate order object. The execution object, either the BuyToCover
// or the Sell is opposite of the entry, so we'll use that and do the opposite.
double commish = execution.Commission * 2;
double profitLoss = getProfitLoss(execution.Order.IsLong ? MarketPosition.Short : MarketPosition.Long, commish, execution.Quantity); // See note above
// Fill listProfits and the appropriate sub-session
dailyListProfits.Add(Math.Round(profitLoss, 2));
// Post strategy profit/loss and consecutive wins/losses tally
postStrategyInfo(execution.Order.Time);
}
else if (execution.Order.OrderState == OrderState.PartFilled)
{
// All we need to do is set the exit prices
for (int i = 0; i < execution.Quantity; ++i)
{
exitPrices.Enqueue(execution.Price);
}
// Note: the reason why it seems like we passing in the opposite direction that you'd expect is because of my design...
// At this point in the vector I no longer have access to the appropriate order object. The execution object, either the BuyToCover
// or the Sell is opposite of the entry, so we'll use that and do the opposite.
double commish = execution.Commission * 2;
double profitLoss = getProfitLoss(execution.Order.IsLong ? MarketPosition.Short : MarketPosition.Long, commish, execution.Quantity); // See note above
dailyListProfits.Add(Math.Round(profitLoss, 2));
postStrategyInfo(execution.Order.Time);
}
}
else if (execution.IsEntryStrategy == true)
{
// Set the commission
if (Commission == 0
&& execution.Commission != 0)
{
Commission = execution.Commission * 2;
}
if (execution.Order.OrderState == OrderState.PartFilled)
{
// This partial fill is the initial entry. We need to set the quantity here for how much the fill actually was
for (int i = 0; i < execution.Quantity; ++i)
{
entryPrices.Enqueue(execution.Price);
}
}
else if (execution.Order.OrderState == OrderState.Filled)
{
for (int i = 0; i < execution.Quantity; ++i)
{
entryPrices.Enqueue(execution.Price);
}
}
}
}
catch (Exception ex)
{
Log(string.Format("OnExecutionUpdate, threw an exception: {0}", ex.Message), LogLevel.Warning);
}
#endregion
if (execution.IsExitStrategy
&& SystemPerformance.AllTrades.Count > 0)
{
lastTrade = SystemPerformance.AllTrades[SystemPerformance.AllTrades.Count - 1];
if (lastTrade != null)
{
// Fill listWinLosses, but don't consider partial fills
if (execution.Order.Quantity == execution.Order.Filled)
{
eTradeResult thisResult = eTradeResult.BREAK_EVEN;
// Fill listWinLosses
if (lastTrade.ProfitCurrency > 0)
{
thisResult = eTradeResult.WINNER;
}
else if (lastTrade.ProfitCurrency < 0)
{
thisResult = eTradeResult.LOSER;
}
listWinLosses.Add(thisResult);
}
}
}
}
catch ( Exception e )
{
Log(string.Format("Error in MartingaleBase.OnExecution: {0}", e.Message), LogLevel.Information);
}
}
private double getProfitLoss(MarketPosition inPos, double commission, int qty)
{
double profitLoss = 0.00;
if (inPos == MarketPosition.Long)
{
for (int i = 0; i < qty; ++i)
{
if (entryPrices.Count > 0
&& exitPrices.Count > 0)
{
double thisEntryPrice = entryPrices.Dequeue();
double thisExitPrice = exitPrices.Dequeue();
profitLoss += (thisExitPrice - thisEntryPrice) * Instrument.MasterInstrument.PointValue;
}
}
}
else if (inPos == MarketPosition.Short)
{
for (int i = 0; i < qty; ++i)
{
if (entryPrices.Count > 0
&& exitPrices.Count > 0)
{
double thisEntryPrice = entryPrices.Dequeue();
double thisExitPrice = exitPrices.Dequeue();
profitLoss += (thisEntryPrice - thisExitPrice) * Instrument.MasterInstrument.PointValue;
}
}
}
// Now, subtract commission
profitLoss -= commission;
return profitLoss;
}
#region postStrategyInfo
/// Called by: OnExecution()
/// OnBarUpdate() at the beginning of the session
/// <summary>
/// This method is used to post strategy wins/losses and also on the next line, consecutive wins or losses
/// in the upper left-corner of the screen.
/// </summary>
protected virtual void postStrategyInfo(DateTime inTime)
{
string output;
NinjaTrader.Gui.Tools.SimpleFont textFontMed = new NinjaTrader.Gui.Tools.SimpleFont("Courier", 14) { Bold = true };
// 1. Post wins/losses in currency
double currency = getDailyProfitLoss();
if (currency < 0)
{
output = "P/L: -$" + Math.Abs(Math.Round(currency, 2)).ToString();
}
else
{
output = "P/L: $" + Math.Round(currency, 2).ToString();
}
// Color the text and the background
Brush outlineColor, textColor;
if (currency >= 0.00)
{
outlineColor = PLBoxColorGains;
textColor = PLBoxTextColorGains;
}
else
{
outlineColor = PLBoxColorLosses;
textColor = PLBoxTextColorLosses;
}
// Post it! (Maybe)
if (ShowStrategyPost)
{
Draw.TextFixed( this, "msg", output, ShowStrategyPostLocation, textColor, textFontMed, outlineColor, outlineColor, PLBoxOpacity );
}
}
protected double getDailyProfitLoss()
{
double currency = 0.00;
foreach (double i in dailyListProfits)
{
currency += i;
}
return currency;
}
#endregion
protected override void OnBarUpdate()
{
if (Bars.IsFirstBarOfSession
&& IsFirstTickOfBar)
{
// Win/loss/consecutive trades stuff
listWinLosses.Clear();
dailyListProfits.Clear();
postStrategyInfo(Time[0]);
}
}
... continues
}

Comment