Traders' Tip text
Since trading high-yield bonds was a simplistic monthly-based strategy, I thought it would be interesting to see how it performed by varying the monthly period by day of month. To do it, I setup of an optimization that calculates the monthly moving average based on the NAV price for each day of the month 1 to 28 as well as for the standard calendar month. The strategy rules remain the same, but the trades trigger on the specified day of the month. Figure 1 shows the daily NAV prices synchronized with the monthly moving average, whereas Figure 2 has the optimization space plotted against the moving average period varying from 3 to 21. It’s interesting to note that trading FAGIX closer to the end (or beginning) of the month correlates with increased profits. Additionally, a motivated trader can boost performance a bit by estimating the NAV and moving average values and trade on the trigger day instead of the next day. Our WealthScript C# code is conveniently available for customers through the Strategy Download feature.
|
Figure 1. Although the chart is Daily, trading occurs only on the transition from one monthly period to the next.
Figure 2. These results indicate that trading FAGIX is more profitable when using shorter periods of the moving average calculated near the end or beginning of the month.
Tip: Don't copy! Instead, use the Strategy Download feature in the Strategy Explorer (Ctrl+O)
using System;
using System.Drawing;
using System.Collections;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class TradingHighYieldBonds : WealthScript
{
private StrategyParameter _period;
private StrategyParameter _atClose;
private StrategyParameter _dayOfMonth;
private DateTime _endofMonth;
private Queue _priceQueue = new Queue(); // FIFO queue
private double AverageQueue()
{
if (_priceQueue.Count == 0) return 0;
double sum = 0;
foreach(Object obj in _priceQueue)
sum += (double)obj;
return sum / _priceQueue.Count;
}
public void SetNextMonth()
{
if (_dayOfMonth.ValueInt == 0) return;
int y = _endofMonth.Year;
int m = _endofMonth.Month;
m++;
if (m > 12)
{
m = 1;
y++;
}
_endofMonth = new DateTime(y, m, _dayOfMonth.ValueInt);
}
public double MonthlyAverage(double currentPrice)
{
if (_priceQueue.Count == _period.ValueInt)
{
_priceQueue.Dequeue();
_priceQueue.Enqueue(currentPrice);
return AverageQueue();
}
else
{
_priceQueue.Enqueue(currentPrice);
return AverageQueue();
}
}
public TradingHighYieldBonds()
{
_period = CreateParameter("MA Period",8,3,21,1);
_atClose = CreateParameter("Trade AtClose",0,0,1,1);
_dayOfMonth = CreateParameter("Day of Month",0,0,28,1);
}
protected override void Execute()
{
HideVolume();
int day = _dayOfMonth.ValueInt;
bool tradeAtClose = _atClose.ValueInt == 1;
bool isLastDayofMonth = false;
_priceQueue = new Queue();
DataSeries ma = new DataSeries(Bars, "Monthly Average(" + _period.ValueInt.ToString()
+ "), dayOfMonth = " + day.ToString());
if (_dayOfMonth.ValueInt != 0)
_endofMonth = new DateTime(Date[0].Year, Date[0].Month, day);
ma[0] = Close[0];
if (Date[0] >= _endofMonth)
SetNextMonth();
for(int bar = 1; bar < Bars.Count; bar++)
{
bool isLastBar = (bar == Bars.Count - 1);
isLastDayofMonth = isLastBar;
int today = Date[bar].Day;
if (!isLastBar)
{
if (day == 0) // test calendar months
isLastDayofMonth = Date[bar].Month != Date[bar + 1].Month;
else if ( today == day || (Date[bar+1] > _endofMonth) )
isLastDayofMonth = true;
}
if (isLastDayofMonth)
{
SetBackgroundColor(bar, Color.FromArgb(30, Color.Blue));
ma[bar] = MonthlyAverage(Close[bar]);
SetNextMonth();
}
else
{
ma[bar] = ma[bar - 1];
continue;
}
if (bar < _period.ValueInt) continue;
if (IsLastPositionActive)
{
Position p = LastPosition;
if (Close[bar] < ma[bar])
{
if (tradeAtClose)
SellAtMarket(bar, p);
else
SellAtMarket(bar + 1, p);
}
}
else if (Close[bar] > ma[bar])
{
if (tradeAtClose)
BuyAtClose(bar);
else
BuyAtMarket(bar + 1);
}
}
PlotSeries(PricePane, ma,Color.Blue, LineStyle.Solid, 2);
}
}
}