@zhaoyang-chen 海龟的Python版出炉。
为方便对比,这里把java、python两种语言代码同时贴出,回测时间及初始资金均使用页面默认的20140104-20150104,100000.0软妹币。
turtle_java
public class TurtleOriginalStrategy implements IHStrategy {
Core talibCore;
//定义全局变量
static int tradedayNum = 0;
static double unit = 0;
static double atr = 0;
static String tradingSignal = "start";
static String preTradingSignal = "";
static int units_hold_max = 4;
static int units_hold = 0;
static double quantity = 0;
static double max_add = 0;
static double firstOpenPrice = 0;
//计算最大最小值
public double[] getExtremem(double[] arrayHighPriceResult, double[] arrayLowPriceResult) {
DescriptiveStatistics forMax = new DescriptiveStatistics();
for (int i = 0; i < arrayHighPriceResult.length-1; i++) {
forMax.addValue(arrayHighPriceResult[i]);
}
double maxResult = forMax.getMax();
DescriptiveStatistics forMin = new DescriptiveStatistics();
for (int i = 0; i < arrayLowPriceResult.length-1; i++) {
forMin.addValue(arrayLowPriceResult[i]);
}
double minResult = forMin.getMin();
double[] forExtremum = new double[2];
forExtremum[0] = maxResult;
forExtremum[1] = minResult;
return forExtremum;
}
//计算Atr以及单位
public double[] getAtrAndUnit(double[] atrArrayResult, MInteger atrLengthResult, double portfolioValueResult) {
double atr = atrArrayResult[atrLengthResult.value-1];
double unit = Math.floor(portfolioValueResult * .01 / atr);
double[] atrAndUnit = new double[2];
atrAndUnit[0] = atr;
atrAndUnit[1] = unit;
return atrAndUnit;
}
//计算止损线价位
public double getStopPrice(double firstOpenPriceResult, int units_hold_result, double atrResult) {
double stopPrice = firstOpenPriceResult - 2*atrResult + (units_hold_result-1)*0.5*atrResult;
return stopPrice;
}
@Override
public void init(IHInformer informer, IHInitializers initializers) {
talibCore = new Core();
int openObserveTime = 55;
int closeObserveTime = 20;
int atrTime = 20;
MInteger atrBegin = new MInteger();
MInteger atrLength = new MInteger();
String stockId = "CSI300.INDX";
initializers.instruments((universe) -> universe.add(stockId));
initializers.events().statistics((stats, info, trans) -> {
//获取组合总价值,包含市场价值与剩余资金
double portfolioValue = info.portfolio().getPortfolioValue();
double[] highPrice = stats.get(stockId).history(openObserveTime+1, HPeriod.Day).getHighPrice();
double[] lowPriceForAtr = stats.get(stockId).history(openObserveTime+1, HPeriod.Day).getLowPrice();
double[] lowPriceForExtremem = stats.get(stockId).history(closeObserveTime+1, HPeriod.Day).getLowPrice();
double[] closePrice = stats.get(stockId).history(openObserveTime+2, HPeriod.Day).getClosingPrice();
double closePriceForAtr[] = new double[closePrice.length-1];
for (int i = 0; i < closePrice.length-1; i++) {
closePriceForAtr[i] = closePrice[i];
}
double[] atrArray = new double[openObserveTime];
//Talib计算N即ATR
RetCode retCode = talibCore.atr(0, openObserveTime-1, highPrice, lowPriceForAtr, closePriceForAtr, atrTime, atrBegin, atrLength, atrArray);
double max = getExtremem(highPrice, lowPriceForExtremem)[0];
double min = getExtremem(highPrice, lowPriceForExtremem)[1];
double atr = atrArray[atrLength.value-1];
informer.info(lowPriceForExtremem[lowPriceForExtremem.length - 1]);
informer.info("#######");
informer.info(max);
informer.info(min);
informer.info(atr);
informer.info("#######");
if (tradingSignal != "start") {
if (units_hold != 0) {
max_add += 0.5 * getAtrAndUnit(atrArray, atrLength, portfolioValue)[0];
}
} else {
max_add = stats.get(stockId).getLastPrice();
}
informer.info(units_hold);
double curPosition = info.position(stockId).getNonClosedTradeQuantity();
double availableCash = info.portfolio().getAvailableCash();
double marketValue = info.portfolio().getMarketValue();
if (curPosition > 0 & stats.get(stockId).getLastPrice() < getStopPrice(firstOpenPrice, units_hold, atr)) {
tradingSignal = "stop";
} else {
if (curPosition > 0 & stats.get(stockId).getLastPrice() < min) {
tradingSignal = "exit";
} else {
if (stats.get(stockId).getLastPrice() > max_add & units_hold != 0 & units_hold < units_hold_max & availableCash > stats.get(stockId).getLastPrice()*unit) {
tradingSignal = "entry_add";
} else {
if (stats.get(stockId).getLastPrice() > max & units_hold == 0) {
max_add = stats.get(stockId).getLastPrice();
tradingSignal = "entry";
}
}
}
}
//informer.info(tradingSignal);
atr = getAtrAndUnit(atrArray, atrLength, portfolioValue)[0];
if (tradedayNum % 5 == 0) {
unit = getAtrAndUnit(atrArray, atrLength, portfolioValue)[1];
}
tradedayNum += 1;
double quantity = unit;
if (tradingSignal != preTradingSignal | (units_hold < units_hold_max & units_hold > 1) | tradingSignal == "stop") {
if (tradingSignal == "entry") {
quantity = unit;
if (availableCash > stats.get(stockId).getLastPrice()*quantity) {
trans.buy(stockId).shares(quantity).commit();
firstOpenPrice = stats.get(stockId).getLastPrice();
units_hold = 1;
informer.info("entrybuy" + quantity);
}
}
if (tradingSignal == "entry_add") {
quantity = unit;
trans.buy(stockId).shares(quantity).commit();
units_hold += 1;
informer.info("entry_addbuy" + quantity);
}
if (tradingSignal == "stop") {
if (/*curPosition marketValue*/ units_hold > 0) {
trans.sell(stockId).shares(quantity).commit();
units_hold -= 1;
informer.info("stop" + quantity);
}
}
if (tradingSignal == "exit") {
if (curPosition > 0) {
trans.sell(stockId).shares(curPosition).commit();
units_hold = 0;
informer.info("exitsell" + curPosition);
}
}
}
preTradingSignal = tradingSignal;
});
}
}
结果:
turtle_python
import numpy as np
import talib
import math
def getExtremem(arrayHighPriceResult, arrayLowPriceResult):
np_arrayHighPriceResult = np.array(arrayHighPriceResult[:-1])
np_arrayLowPriceResult = np.array(arrayLowPriceResult[:-1])
maxResult = np_arrayHighPriceResult.max()
minResult = np_arrayLowPriceResult.min()
return [maxResult, minResult]
def getAtrAndUnit(atrArrayResult, atrLengthResult, portfolioValueResult):
atr = atrArrayResult[atrLengthResult-1]
unit = math.floor(portfolioValueResult * .01 / atr)
return [atr, unit]
def getStopPrice(firstOpenPriceResult, units_hold_result, atrResult):
stopPrice = firstOpenPriceResult - 2*atrResult + (units_hold_result-1)*0.5*atrResult
return stopPrice
def init(context):
context.tradedayNum = 0
context.unit = 0
context.atr = 0
context.tradingSignal = 'start'
context.preTradingSignal = ''
context.units_hold_max = 4
context.units_hold = 0
context.quantity = 0
context.max_add = 0
context.firstOpenPrice = 0
context.s = 'CSI300.INDX'
update_universe([context.s])
context.openObserveTime = 55;
context.closeObserveTime = 20;
context.atrTime = 20;
def handle_bar(context, bar_dict):
portfolioValue = context.portfolio.portfolio_value
highPrice = history(context.openObserveTime+1, '1d', 'high')[context.s]
lowPriceForAtr = history(context.openObserveTime+1, '1d', 'low')[context.s]
lowPriceForExtremem = history(context.closeObserveTime+1, '1d', 'low')[context.s]
closePrice = history(context.openObserveTime+2, '1d', 'close')[context.s]
closePriceForAtr = closePrice[:-1]
atrArray = talib.ATR(highPrice.values, lowPriceForAtr.values, closePriceForAtr.values, timeperiod=context.atrTime)
maxx = getExtremem(highPrice.values, lowPriceForExtremem.values)[0]
minn = getExtremem(highPrice.values, lowPriceForExtremem.values)[1]
atr = atrArray[-2]
if (context.tradingSignal != 'start'):
if (context.units_hold != 0):
context.max_add += 0.5 * getAtrAndUnit(atrArray, atrArray.size, portfolioValue)[0]
else:
context.max_add = bar_dict[context.s].last
curPosition = context.portfolio.positions[context.s].quantity
availableCash = context.portfolio.cash
marketValue = context.portfolio.market_value
if (curPosition > 0 and bar_dict[context.s].last < minn):
context.tradingSignal = 'exit'
else:
if (curPosition > 0 and bar_dict[context.s].last < getStopPrice(context.firstOpenPrice, context.units_hold, atr)):
context.tradingSignal = 'stop'
else:
if (bar_dict[context.s].last > context.max_add and context.units_hold != 0 and context.units_hold < context.units_hold_max and availableCash > bar_dict[context.s].last*context.unit):
context.tradingSignal = 'entry_add'
else:
if (bar_dict[context.s].last > maxx and context.units_hold == 0):
context.max_add = bar_dict[context.s].last
context.tradingSignal = 'entry'
atr = getAtrAndUnit(atrArray, atrArray.size, portfolioValue)[0]
if context.tradedayNum % 5 == 0:
context.unit = getAtrAndUnit(atrArray, atrArray.size, portfolioValue)[1]
context.tradedayNum += 1
context.quantity = context.unit
if (context.tradingSignal != context.preTradingSignal or (context.units_hold < context.units_hold_max and context.units_hold > 1) or context.tradingSignal == 'stop'):
if context.tradingSignal == 'entry':
context.quantity = context.unit
if availableCash > bar_dict[context.s].last*context.quantity:
order_shares(context.s, context.quantity)
context.firstOpenPrice = bar_dict[context.s].last
context.units_hold = 1
if context.tradingSignal == 'entry_add':
context.quantity = context.unit
order_shares(context.s, context.quantity)
context.units_hold += 1
if context.tradingSignal == 'stop':
if (context.units_hold > 0):
order_shares(context.s, -context.quantity)
context.units_hold -= 1
if context.tradingSignal == 'exit':
if curPosition > 0:
order_shares(context.s, -curPosition)
context.units_hold = 0
context.preTradingSignal = context.tradingSignal
结果:
PLUS:
1.没用到python的一些黑法术的情况下java代码190行,python代码120行。
2.java编译小猫耳朵猛抖8下,python编译小猫耳朵猛抖了13下。
回测收益回测年化收益基准收益AlphaBetaSharpe最大回撤
31.685%32.190%56.904%0.03660.45372.5366-3.720%
Created with Highstock 4.2.4缩放1月3月6月1年全部从2014-01-06到2014-12-31累计收益2014-022014-042014-062014-082014-102014-12-10.00%-5.00%0%5.00%10.00%15.00%20.00%25.00%30.00%35.00%40.00%45.00%50.00%55.00%60.00%65.00%