I am sorry to ask you so many questions, but please tell me how to convert this wealth-lab code to QS 194.
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2328254
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using WealthLab.Rules;
using Community.Indicators;
namespace WealthLab.Strategies
{
#region Helper classes
public class RankHolder
{
public string symbol;
public double val;
public double rank;
}
public class MomHolder : RankHolder
{
public MomHolder( string symbol, double val )
{
this.symbol = symbol;
this.val = val;
}
}
public class VolHolder : RankHolder
{
public VolHolder( string symbol, double val )
{
this.symbol = symbol;
this.val = val;
}
}
public class CorrHolder : RankHolder
{
public CorrHolder( string symbol, double val )
{
this.symbol = symbol;
this.val = val;
}
}
public class HolderContainer
{
public MomHolder momHolder;
public VolHolder volHolder;
public CorrHolder corrHolder;
}
#endregion Helper classes
/********************
0 - 0.32 0%
0.33 - 0.49 50%
0.50 - 0.67 67%
0.68 - 1.0 100%
*********************/
public class CombinedRanking : WealthScript, IComparer<RankHolder>, IComparer<MomHolder>, IComparer<VolHolder>, IComparer<CorrHolder>
{
StrategyParameter numberOfSymbols;
StrategyParameter momPeriod_;
StrategyParameter volPeriod_;
StrategyParameter corrPeriod_;
StrategyParameter momWeight_;
StrategyParameter volWeight_;
StrategyParameter corrWeight_;
public CombinedRanking()
{
numberOfSymbols = CreateParameter("n Symbols", 3, 2, 5, 1);
momPeriod_ = CreateParameter("Mom Period", 4, 4, 4, -1);
volPeriod_ = CreateParameter("Vol Period", 4, 4, 4, -1);
corrPeriod_ = CreateParameter("Corr Period", 4, 4, 4, -1);
momWeight_ = CreateParameter("Mom Weight", 1, 1, 1, -1);
volWeight_ = CreateParameter("Vol Weight", 0.5, 0.5, 0.5, -1);
corrWeight_ = CreateParameter("Corr Weight", 0.5, 0.5, 0.5, -1);
}
/* Add this BuyAtMarket method and nothing else needs to be changed */
// It uses a limit order that executes at the opening price so that the basis price is the same as the execution price
Position BuyAtClose(int n, string sigName = "")
{
if (n >= Bars.Count)
return base.BuyAtClose(n, sigName); // create market order alert
else
return BuyAtLimit(n, Close[n], sigName);
}
protected override void Execute()
{
//Clear cached symbol list so they can be re-requested with synchronization
ClearExternalSymbols();
int number = numberOfSymbols.ValueInt;
int momPeriod = momPeriod_.ValueInt;
int volPeriod = volPeriod_.ValueInt;
int corrPeriod = corrPeriod_.ValueInt;
double wM = momWeight_.Value;
double wV = volWeight_.Value;
double wC = corrWeight_.Value;
//Execute rotation strategy
Dictionary<string, HolderContainer> holders = new Dictionary<string, HolderContainer>();
List<MomHolder> listMom = new List<MomHolder>();
List<VolHolder> listVol = new List<VolHolder>();
List<CorrHolder> listCorr = new List<CorrHolder>();
List<RankHolder> list = new List<RankHolder>(); // the combined list
for(int bar = Math.Max( momPeriod, Math.Max(volPeriod, corrPeriod)); bar < Bars.Count; bar++)
{
listMom.Clear();
listVol.Clear();
listCorr.Clear();
list.Clear();
holders.Clear();
foreach(string symbol in DataSetSymbols)
{
SetContext(symbol, true);
HolderContainer hc = new HolderContainer();
MomHolder mh = null;
VolHolder vh = null;
CorrHolder ch = null;
if (Bars.FirstActualBar < bar)
{
SetScaleMonthly();
DataSeries mom = ROC.Series(Bars.Close, momPeriod);
RestoreScale();
mom = Synchronize(mom);
mh = new MomHolder( symbol, mom[bar]);
listMom.Add(mh);
hc.momHolder = mh;
DataSeries vol = -1 * StdDev.Series( ROC.Series( Bars.Close, 1 ), volPeriod * 21, StdDevCalculation.Population ) * Math.Sqrt(252); // lowest is best
vh = new VolHolder( symbol, vol[bar]);
listVol.Add(vh);
hc.volHolder = vh;
ch = new CorrHolder( symbol, -1 * CalcPfC(Bars.Close, corrPeriod * 21, bar));
listCorr.Add(ch);
hc.corrHolder = ch;
holders.Add(symbol, hc);
}
RestoreContext();
}
listMom.Sort(this); // highest is best
listVol.Sort(this); // lowest is best
listCorr.Sort(this); // lowest is best
// Generate Momentum Ranks
for (int i=0; i < listMom.Count; i++)
{
listMom[i].rank = i + 1;
}
// Generate Volatility Ranks
for (int i=0; i < listVol.Count; i++)
{
listVol[i].rank = i + 1;
}
// Generate Correlation Ranks
for (int i=0; i < listCorr.Count; i++)
{
listCorr[i].rank = i + 1;
}
// Iterate through holder dictionary, determining MVC rank for each symbol and creating list of MVC RankHolders
foreach(KeyValuePair<string, HolderContainer> entry in holders)
{
RankHolder rh = new RankHolder();
rh.symbol = entry.Key;
HolderContainer hc = entry.Value;
rh.val = (wM + 0.001) * hc.momHolder.rank + wV * hc.volHolder.rank + wC * hc.corrHolder.rank;
list.Add(rh);
}
list.Sort(this); // lowest is best
// Generate MVC Ranks
/*
for (int i=0; i < list.Count; i++)
{
list[i].rank = i + 1;
}
*/
//keep top number (3 is initial default) only
for(int i = list.Count - 1; i >= number; i--)
list.RemoveAt(i);
//Filter/Remove the ones with Absolute Momentum < 0
for (int i=0; i < list.Count; i++)
{
RankHolder rh = list[i];
if (holders[rh.symbol].momHolder.val < 0)
{
list.RemoveAt(i);
}
}
//Close positions that are not in the new lowest 3
for(int pos = ActivePositions.Count - 1; pos >= 0; pos--)
{
Position p = ActivePositions[pos];
bool keepPosition = false;
foreach(RankHolder holder in list)
if (holder.symbol == p.Bars.Symbol)
{
keepPosition = true;
break;
}
if (
(!keepPosition) &&
DateRules.IsLastTradingDayOfMonth(Bars.Date[bar])
)
SellAtClose(bar, p, "Rotation");
}
double posPriority = (double) 1/number;
PrintDebug("Priority: "+ posPriority);
PrintDebug("Count: "+ list.Count);
PrintDebug("Number: "+ number);
double cashPriority = 1 - (list.Count * posPriority);
//Buy new positions
foreach(RankHolder holder in list)
{
bool buyPosition = true;
foreach(Position p in ActivePositions)
if (p.Bars.Symbol == holder.symbol)
{
buyPosition = false;
break;
}
if (
(buyPosition) &&
(DateRules.IsLastTradingDayOfMonth(Bars.Date[bar]))
)
{
SetContext(holder.symbol, true);
if (BuyAtClose(bar, "Rotation") != null)
{
LastPosition.Priority = posPriority;
}
RestoreContext();
}
}
}
PrintStatusBar("Strategy completed!");
}
public double CalcPfC( DataSeries close, int length, int bar )
{
// This function takes a symbol and a list of symbols
// and calculates the average correlation of the
// symbols TS to the other symbols TS's (TS:TimeSeries)
double corSum = 0;
int j=0;
foreach(string ticker in DataSetSymbols)
{
Bars tickerBars = GetExternalSymbol(ticker, true);
corSum = corSum + Correlation.Series(tickerBars.Close , close, length )[bar];
j++;
/*
SetContext(ticker, true);
if (Bars.FirstActualBar < bar)
{
corSum = corSum + Correlation.Series(Bars.Close , close, length )[bar];
}
RestoreContext();
j++;
*/
}
// take out the diagonal of the correlation matrix and average
return (corSum - 1)/ (j -1);
}
#region IComparer implementation
public int Compare(RankHolder item1, RankHolder item2)
{
return item1.val.CompareTo(item2.val); // ascending -> worst - best
}
public int Compare(MomHolder item1, MomHolder item2)
{
return item2.val.CompareTo(item1.val); // descending -> best - worst
}
public int Compare(VolHolder item1, VolHolder item2)
{
return item2.val.CompareTo(item1.val); // descending -> best - worst
}
public int Compare(CorrHolder item1, CorrHolder item2)
{
return item2.val.CompareTo(item1.val); // descending -> best - worst
}
#endregion
}
}