This PosSizer was created by user
Leszek Mazur. It facilitates backtesting of balancing a portfolio. It works in conjunction with specially prepared Strategy script and allows to optimize percentages of different symbols as well as the holding period. Below is the example Strategy code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using WealthLab.PosSizers;
namespace WealthLab.Strategies
{
public class RebalanceStrategy : WealthScript
{
/////////////////////////////////
// config
/////////////////////////////////
bool debugOut = true;
string entry1symbol = "Selected Symbol";
string entry2symbol = "IEF";
string entry3symbol = "GLD";
int numEntries = 3;
/////////////////////////////////
StrategyParameter entry1portion;
StrategyParameter entry2portion;
StrategyParameter entry3portion;
StrategyParameter holdingPeriod;
Tuple<string, double>[] symInfos;
public RebalanceStrategy()
{
entry1portion = CreateParameter(entry1symbol + " portfolio percentage", 30, 0, 100, 5);
entry2portion = CreateParameter(entry2symbol + " portfolio percentage", 30, 0, 100, 5);
entry3portion = CreateParameter(entry3symbol + " portfolio percentage", 30, 0, 100, 5);
holdingPeriod = CreateParameter("holding period", 60, 5, 200, 5);
symInfos = new Tuple<string, double>[numEntries];
MS123.PosSizers.BalancingPosSizer.SymInfos = symInfos;
}
private void OnLog(object sender, MS123.PosSizers.SizerMessageEventArgs args)
{
PrintDebug(args.Message);
}
protected override void Execute()
{
if (debugOut)
{
ClearDebug();
MS123.PosSizers.BalancingPosSizer.ResetLog();
MS123.PosSizers.BalancingPosSizer.LogMessage += OnLog;
}
// validation
double total = 0;
total += entry1portion.Value;
total += entry2portion.Value;
total += entry3portion.Value;
if(total > 100)
{
if (debugOut)
PrintDebug("Bailing out due to total percentage of holdings adding up to more than 100%, total=" + total);
return;
}
entry1symbol = Bars.Symbol;
Bars entry1bars = Bars;
Bars entry2bars = GetExternalSymbol(entry2symbol, true);
Bars entry3bars = GetExternalSymbol(entry3symbol, true);
ChartPane pane0 = null;
ChartPane pane1 = null;
symInfos[0] = new Tuple<string, double>(entry1symbol,entry1portion.Value/100.0);
symInfos[1] = new Tuple<string, double>(entry2symbol,entry2portion.Value/100.0);
symInfos[2] = new Tuple<string, double>(entry3symbol,entry3portion.Value/100.0);
pane0 = CreatePane(75, true, true);
PlotSeries(pane0, entry2bars.Close, Color.BurlyWood, LineStyle.Solid, 2);
pane1 = CreatePane(75, true, true);
PlotSeries(pane1, entry3bars.Close, Color.BurlyWood, LineStyle.Solid, 2);
int holding = 0;
for (int bar = 0; bar < Bars.Count; bar++)
{
if(0 == holding)
{
if(entry1bars.FirstActualBar <= bar)
{
SetContext(symInfos[0].Item1,true);
BuyAtMarket(bar + 1, symInfos[0].Item1 + " buy");
if (debugOut)
PrintDebug(symInfos[0].Item1 + " Bought on " + Bars.Date[bar + 1]);
}
if(entry2bars.FirstActualBar <= bar)
{
SetContext(symInfos[1].Item1,true);
BuyAtMarket(bar + 1, symInfos[1].Item1 + " buy");
if (debugOut)
PrintDebug(symInfos[1].Item1 + " Bought on " + Bars.Date[bar + 1]);
}
if(entry3bars.FirstActualBar <= bar)
{
SetContext(symInfos[2].Item1,true);
BuyAtMarket(bar + 1, symInfos[2].Item1 + " buy");
if (debugOut)
PrintDebug(symInfos[2].Item1 + " Bought on " + Bars.Date[bar + 1]);
}
holding++;
}
else if(holding >= holdingPeriod.ValueInt)
{
foreach(Position p in Positions)
{
if(p.Active)
{
SellAtMarket(bar + 1, p, p.Symbol + " sell");
if (debugOut)
PrintDebug(p.Symbol + " Sold on " + Bars.Date[bar + 1] + ", profit = " + p.NetProfitAsOfBarPercent(bar + 1));
}
}
holding = 0;
}
else
holding++;
}
}
}
}