Position Management

The Position Management category contains methods you can use to access and manipulate Positions that have been created by the Strategy.

BuyAtMarket

ActivePositions Property

IList<Position> ActivePositions

Returns the number of Positions that are currently still active.  Use the Positions property to access the collection of actual Positions, and check the Active property of each Position to determine if it is active or not.


Example

protected override void Execute(){
    ChartPane RSIPane = CreatePane(  50, true, true );
    PlotSeriesOscillator( RSIPane, RSI.Series( Close, 20 ), 70, 30,
        Color.Red, Color.Blue, Color.MidnightBlue, WealthLab.LineStyle.Dashed, 1 );

    for(int bar = 60; bar < Bars.Count; bar++)
    {
        if ( CrossUnder( bar, RSI.Series( Close, 20 ), 30 ) )
        {
            // Here we limit the system to 3 active Positions max
            if ( ActivePositions.Count < 3 )
                BuyAtMarket( bar+1 );
        }

        if ( ( ActivePositions.Count > 0 ) && CrossOver( bar, RSI.Series( Close, 20 ), 50 ) )
        {
            // Let's work directly with the list of active positions, introduced in WL5
            for( int p = ActivePositions.Count - 1; p > -1 ; p-- )
                SellAtMarket( bar+1, ActivePositions[p] );
        }
    }
}
BuyAtMarket

Alerts Property

IList<Alert> Alerts

Returns a list of Alerts objects that represent all of the alerts that have been triggered by the Strategy so far.  Use the Count proprety of the Alerts list to determine how many Alerts are in the list.  Access the individual Alerts via the [] indexer.

Remarks


Example

protected override void Execute(){
    // Alert generating code
    for(int bar = 3; bar < Bars.Count; bar++)
    {
        if (!IsLastPositionActive)
        {
            // Two consecutive lower closes
            if( CumDown.Series(Close, 1)[bar] >= 2 )
                BuyAtLimit( bar+1, Bars.High[bar], "Consecutive close lower" );
        } else
        if (IsLastPositionActive)
        {
            SellAtMarket( bar+1, LastPosition, "Exit next day" );
        }
    }
    
    // Alert properties
    if( Alerts.Count > 0 )
    {
        for( int i = 0; i < Alerts.Count; i++ )
        {
            WealthLab.Alert a = Alerts[i];

            PrintDebug( "Account: " + a.Account );  // blank string
            PrintDebug( "AlertDate: " + a.AlertDate );                                                      
            PrintDebug( "AlertType: " + a.AlertType );
            PrintDebug( "BarInterval: " + a.BarInterval );  
            PrintDebug( "BasisPrice: " + a.BasisPrice );
            PrintDebug( "OrderType: " + a.OrderType ); 
            PrintDebug( "Last close: " + a.Bars.Close[Bars.Count-1] );  // Access Bars object                                 
            PrintDebug( "PositionType: " + a.PositionType );   
            PrintDebug( "Price: " + a.Price );
            PrintDebug( "RiskStopLevel: " + a.RiskStopLevel );
            PrintDebug( "Symbol: " + a.Symbol );
            PrintDebug( "Scale: " + a.Scale );
            PrintDebug( "Shares: " + a.Shares );
            PrintDebug( "SignalName: " + a.SignalName );

            try 
            {
                PrintDebug( "Position: " + a.Position );
            }
            catch 
            {
                PrintDebug( "Position: entry" );
            }
        }
    }
}
BuyAtMarket

ClearPositions

void ClearPositions();

Clears all of the trading system Positions that have been generated so far by the Strategy.

Remarks


Example

protected override void Execute(){
    // Calculate the win/loss ratio of the system taking all trades.
    // Then re-run the system, but only take trades when the prior
    // win/loss ratio was above 50%. }
    int Winners, Trades;
    ChartPane WinLossPane = CreatePane( 50, false, true );
    ChartPane CMOPane = CreatePane( 40, true, true );
    PlotSeries( CMOPane, CMO.Series( Close, 20 ), Color.Blue, WealthLab.LineStyle.Solid, 2 );
    SetPaneMinMax( WinLossPane, 0, 100 );
    DataSeries WinLoss = new DataSeries( Bars, "WinLoss" );
    for(int bar = 60; bar < Bars.Count; bar++)
    {
        Winners = 0;
        Trades = 0;
        foreach( Position p in Positions )
            if ( !p.Active )
            {
                Trades++;
                if ( p.NetProfit > 0 )
                    Winners++;
            }          
        if ( Trades > 0 )
        {
            WinLoss[bar] = Winners * 100 / Trades;
        }
        if ( CrossOver( bar, CMO.Series( Close, 20 ), -40 ) )
            BuyAtMarket( bar+1, "CMO" ); else 
            if ( CrossUnder( bar, CMO.Series( Close, 20 ), 40 ) )
                SellAtMarket( bar+1, Position.AllPositions, "CMO" );
    }
    // Plot the Win/Loss Ratio
    PlotSeries( WinLossPane, WinLoss, Color.Green, WealthLab.LineStyle.Histogram, 5 );
    DrawLabel( WinLossPane, "Win/Loss Ratio", Color.Green );
    // Clear the trades
    ClearPositions();
    // Execute the system again, but only take the trade if the win/loss ratio was above 50
    for(int bar = 60; bar < Bars.Count; bar++)
    {
        if ( CrossOver( bar, CMO.Series( Close, 20 ), -40 ) )
        {
            if ( WinLoss[bar] > 50 )
                BuyAtMarket( bar+1, "CMO" );
        }
        else if ( CrossUnder( bar, CMO.Series( Close, 20 ), 40 ) )
            SellAtMarket( bar+1, Position.AllPositions, "CMO" );
    }
}
BuyAtMarket

IsLastPositionActive Property

bool IsLastPositionActive

Indicates whether the most recently established Position (if any) is active or closed.  If there were no Positions created yet the property returns false.


Example

protected override void Execute(){
    for(int bar = 20; bar < Bars.Count; bar++)
    {
        if (IsLastPositionActive)
        {
            double entryPrice = LastPosition.EntryPrice;
            //code your exit rules here
            SellAtStop( bar, LastActivePosition, entryPrice*0.8, "20% stop loss" );
            SellAtLimit( bar, LastActivePosition, entryPrice*1.07, "7% profit target" );
        }
        else
        {
            if ( CrossOver( bar, RSI.Series( ( (High+Low)/2), 40 ), 60 ) )
                BuyAtMarket( bar+1, "Buy Strength" );
        }
    }
}
BuyAtMarket

LastActivePosition Property

Position LastActivePosition

Returns the most recently created Position object that is still active (has not yet been sold or covered).  If there are no open Positions, LastActivePosition returns null.

Remarks

  • See the documentation for the Position object for more information on its properties and methods.

Example

protected override void Execute(){
    int period = 14;
    int oversoldLevel = 30;
    int overboughtLevel = 70;
    DataSeries rsi = RSI.Series( Close, period );
    
    //Trading system loop
    for (int bar = rsi.FirstValidValue; bar < Bars.Count; bar++)
    {
        if ( LastActivePosition == null )
        {
            //Buy when RSI crosses above oversold level
            if( CrossOver( bar, rsi, oversoldLevel ) )
                BuyAtMarket( bar+1, "RSI Oversold");
        } else
        {
            // Sell when it crosses below oversold level
            if( CrossUnder( bar, rsi, overboughtLevel ) )
                SellAtMarket( bar+1, LastActivePosition );
        }
    }
}
BuyAtMarket

LastPosition Property

Position LastPosition

Returns the most recently created Position object.  If there were no Positions created yet, the property returns null.

Remarks

  • See the Position object documentation for information about its properties and methods.

Example

protected override void Execute(){
    PlotSeries( PricePane, SMA.Series( Close, 20 ), Color.Red, WealthLab.LineStyle.Solid, 1 );
    PlotSeries( PricePane, SMA.Series( Close, 10 ), Color.Blue, WealthLab.LineStyle.Solid, 1 );
    for(int bar = 20; bar < Bars.Count; bar++)
    {
        if ( CrossOver( bar, SMA.Series( Close, 10 ), SMA.Series( Close, 20 ) ) )
            BuyAtMarket( bar+1 ); else
        if ( CrossOver( bar, SMA.Series( Close, 20 ), SMA.Series( Close, 10 ) ) )
            SellAtMarket( bar+1, LastPosition );
    }
}
BuyAtMarket

MarketPosition Property

double MarketPosition

Returns the net number of shares that the Strategy has in open Positions.  Long Positions add their shares to this value, and short Positions subtract their shares.

Remarks

  • In portfolio testing mode, Strategies are pre-executed using a 1 share per Position sizing strategy, then position sizing is applied after the fact.  For this reason, MarketPosition will always count a Position's shares as 1 when testing in this mode.

Example

protected override void Execute(){
    DataSeries hMA = WMA.Series( Close, 30 );
    DataSeries mp = new DataSeries( Bars, "Market Position" );

    for(int bar = hMA.FirstValidValue; bar < Bars.Count; bar++)
    {
        // Record the number of shares in open positions at each bar as a Data Series.
        mp[bar] = MarketPosition;

        {
            // Buy on a crossover of a 30-period weighted moving average
            // Hold up to 5 positions
            if( CrossOver( bar, Close, hMA[bar-1] ) )
            if( ActivePositions.Count < 5 )
                BuyAtMarket(bar + 1);
        }
        {
             // Sell all positions on a crossunder of a 30-period WMA
            if( CrossUnder( bar, Close, hMA[bar-1] ) )
                foreach ( Position p in Positions )
                    if ( p.Active )
                        SellAtMarket( bar+1, p );
        }
    }
    
    ChartPane ProfitPane = CreatePane( 30, true, true );
    PlotSeries( ProfitPane, mp, Color.DarkGreen, WealthLab.LineStyle.Histogram, 1 ); //'Open Profit' 
    PlotSeries( PricePane, hMA, Color.Blue, WealthLab.LineStyle.Solid, 2 );
}
BuyAtMarket

Positions Property

IList<Position> Positions

Returns the Positions that have been established to date by the Strategy (via BuyAtMarket, ShortAtMarket, etc.)  Each item in this list is a Position object that represents either a long or a short position.

Remarks

  • Use Positions.Count property to determine how many Positions are in the list.
  • See the documentation for the Position object to learn about its properties and methods.

Example

protected override void Execute(){
    int lastBarBought = 0;

    //Trading system loop
    for (int bar = 41; bar < Bars.Count; bar++)
    {
        // Build up a position buying 40-period High breakouts
        if( ( LastActivePosition != null && LastActivePosition.EntryPrice > Bars.Close[bar] ) || LastActivePosition == null )
            if( bar >= lastBarBought + 9 )
            {
                if( BuyAtStop( bar+1, Highest.Series( High, 40 )[bar], "Breakout" ) != null )
                    lastBarBought = bar+1;
            }
        
        // Exit positions
        foreach( Position p in Positions )
            if ( p.Active )
                SellAtStop( bar+1, p, Lowest.Series( Low, 20 )[bar], "Breakdown" );
    }
}
BuyAtMarket

SplitPosition

Position SplitPosition(Position position, double percentToRetain);

Splits a single Position into two Positions, allowing you to effectively scale out of a single Position using more than one exit.  The position parameter contains the Position object that you wish to split.  The percentToRetain parameter contains the percentage of shares that you wish to remain in the original Position object.  SplitPosition returns a new Position object that contains the remaining shares.  This new Position object is also added to the end of the Positions list.

Remarks

  • Problem: Having split a Position into two with SplitPosition, the following properties incorrectly report 0 or NaN for the first part of the splitted Position if Strategy is run in a Portfolio Simulation mode: MFEAsOfBarPercent, MFEAsOfBar, MAEAsOfBarPercent, MAEAsOfBar, NetProfitAsOfBarPercent, NetProfitAsOfBar.
  • Partial workaround: Switch to a Raw Profit position sizing mode.
  • Problem: in Portfolio Simulation Mode, expect that either all or none of the Positions resulting from SplitPosition will be rejected according to the amount of cash available on the entry bar. While building the equity curve, Wealth-Lab treats each split Position as a separate Position competing for cash, and, if Position.Priority is random each Position has a random chance of being selected or rejected in a high-exposure MSB Portfolio Simulation.
  • Partial workaround: To help reduce the frequency (but in no way guarantee) of taking different actions for split Positions, assign the same Priority to the new split Position as the original after calling SplitPosition (see code snippet below).

Example

protected override void Execute(){
bool soldForProfit = false;

    for(int bar = 50; bar < Bars.Count; bar++)
    {
        if ( ActivePositions.Count > 0 )
        {
            // Split the position to protect large gain
            if ( LastPosition.MFEAsOfBarPercent( bar ) > 20 )
            {
                if (!soldForProfit) {
                    Position p = LastPosition;                    // Position to split
                    Position s = SplitPosition( p, 49.99 );        // The new Position
                    s.Priority = p.Priority;                     // Assign the same Priority as the original Position
                    soldForProfit = SellAtMarket( bar+1, s, "Secure large profit" );
                }
                else {                                                       
                    // Exit the rest on a tight stop
                    SellAtStop( bar+1, LastActivePosition, Lowest.Series( Low,10 )[bar], "the rest" );
                }                                                                                               
            }
        } 
        else
        {
            BuyAtStop( bar+1, Highest.Series( High, 50 )[bar], "no-frills breakout" );
            soldForProfit = false;
        }
    }
}