Reply

Is the Sortino Ratio script right?

2 replies

eastpeace

Customer, bbp_participant, community, sq-ultimate, 305 replies.

Visit profile

4 years ago #257305

`/*
* Copyright (c) 2017-2018, StrategyQuant – All rights reserved.
*
* Code in this file was made in a good faith that it is correct and does what it should.
* If you found a bug in this code OR you have an improvement suggestion OR you want to include
* your own code snippet into our standard library please contact us at:
* https://roadmap.strategyquant.com
*
* This code can be used only within StrategyQuant products.
* Every owner of valid (free, trial or commercial) license of any StrategyQuant product
* is allowed to freely use, copy, modify or make derivative work of this code without limitations,
* to be used in all StrategyQuant products and share his/her modifications or derivative work
* with the StrategyQuant community.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package SQ.Columns.Databanks;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.strategyquant.lib.L;
import com.strategyquant.lib.SQTime;
import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.SettingsMap;
import com.strategyquant.tradinglib.DatabankColumn;
import com.strategyquant.tradinglib.Directions;
import com.strategyquant.tradinglib.Order;
import com.strategyquant.tradinglib.OrdersList;
import com.strategyquant.tradinglib.PlTypes;
import com.strategyquant.tradinglib.SQStats;
import com.strategyquant.tradinglib.StatsTypeCombination;
import com.strategyquant.tradinglib.ValueTypes;

import SQ.Functions.StatFunctions;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.ints.Int2DoubleAVLTreeMap;

public class Sortino extends DatabankColumn {
public static final Logger Log = LoggerFactory.getLogger(“Sortino”);

private final long DAY_DURATION = 24 * 60* 60 * 1000;

//————————————————————————
//————————————————————————
//————————————————————————

public Sortino() {
super(L.t(“Sortino Ratio”), DatabankColumn.Decimal2, ValueTypes.Maximize, 0, -1, 1);

this.setTooltip(L.t(“Sortino Ratio (annualized)”));

// restrict Sortino computation only for money PL Type (we’ll not compute separate Sortino for % or pips results, it is as same as for money)
setPLTypeRestrictions(PlTypes.Money);

// restrict Sortino computation only for Both direction, we’ll not compute Sharpe for Long only or Short only results
setDirectionRestrictions(Directions.Both);
}

//————————————————————————

@Override
public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception {

DoubleArrayList dailyReturnsPct = computeDailyReturn(ordersList);

double returnsMean = StatFunctions.computeAverage(dailyReturnsPct);

double returnsStddev = computeLowStdev(returnsMean, dailyReturnsPct,0,-1);

double Sortino = Math.sqrt(252) * SQUtils.safeDivide(returnsMean, returnsStddev);

return round2(Sortino);
}

//————————————————————————

private DoubleArrayList computeDailyReturn(OrdersList ordersList) {
if(ordersList.size() == 0) {
return null;
}

// we take 5% yearly profit as benchmark for computing Sortino Ratio
double benchmark = 0.05/252;

// compute first and last date
long firstDate = Long.MAX_VALUE;
long lastDate = -1;

for(int i=0; i<ordersList.size(); i++) {
Order o = ordersList.get(i);

if(o.CloseTime < firstDate) {
firstDate = o.CloseTime;
}

if(o.CloseTime > lastDate) {
lastDate = o.CloseTime;
}
}

// fix it so that it starts on Monday
firstDate = SQTime.correctDayStart(firstDate);
int firstDateDow = SQTime.getDayOfWeek(firstDate);

// create array of returns for every day that is not saturday/sunday
DoubleArrayList returns = new DoubleArrayList();
int index = 1;
long startDate = firstDate;
while(true) {
int weekIndex = index+firstDateDow-1;
if(weekIndex > 7) {
int weeks = weekIndex / 7;
weekIndex -= weeks*7;
}
if(weekIndex % 6 != 0 && weekIndex % 7 != 0) {
// skip Saturdays and Sundays
returns.add(-benchmark);
}

index++;

startDate += DAY_DURATION;

if(startDate > lastDate) {
break;
}
}

// now go through orders and add PctPL for every order
for(int i=0; i<ordersList.size(); i++) {
Order o = ordersList.get(i);

int dow = SQTime.getDayOfWeek(o.CloseTime);
if(dow == 6 || dow == 7) {
// skip Saturdays and Sundays
continue;
}

long closeTime = SQTime.correctDayStart(o.CloseTime);

index = (int) ((closeTime – firstDate) / DAY_DURATION);

// deduct weekends
if(index > firstDateDow) {
index -= 2;
}
int weeks = index / 7;
index -= weeks*2;

if(index < 0 || index >= returns.size()) {
continue;
}

double currentDayPL = returns.getDouble(index);
returns.set(index, currentDayPL+o.PctPL);
}

return returns;
}

public static double computeLowStdev(double mean, DoubleArrayList values, int from, int to ) {
if(values == null || values.size() == 0) {
return 0;
}

if(to == -1) {
to = values.size();
}

double sum = 0, stdev, pl;
for (int i = from; i < to; i++) {
pl = values.getDouble(i);
sum += Math.pow((Math.min(pl – mean,0)), 2d);
}
stdev = (double) Math.sqrt(sum / ((double) (to – from)));
return (stdev);
}
}

0

tomas262

Administrator, sq-ultimate, 2 replies.

Visit profile

4 years ago #257432

Hello, I have checked but this returns 0 for me. I guess there is something wrong with the computeLowStdev function but I am not a Java coder myself. Developers will review the Sortino code so it could be added into following SQ builds

0

eastpeace

Customer, bbp_participant, community, sq-ultimate, 305 replies.

Visit profile

3 years ago #258033

 

It has result. Maybe you need restart SQ and do retest.

But I just copied the Sharpe ratio and am not sure if it is correct.  Especially I feel confused about the statistics of daily return.

0

Viewing 2 replies - 1 through 2 (of 2 total)