海龟交易系统的 Python 完全版

2016-11-15 16:35:53 +08:00
 thinkingmind

**新鲜出品:

新鲜出品:https://www.ricequant.com/community/topic/180//2?utm_source=v2ex?

海龟的 Python 版出炉。 为方便对比,这里把 java 、 python 两种语言代码同时贴出,回测时间及初始资金均使用页面默认的 20140104-20150104 , 100000.0 软妹币。

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 下。

新鲜出品:https://www.ricequant.com/community/topic/180//2?utm_source=v2ex?

1091 次点击
所在节点    推广
2 条回复
rust
2016-11-15 16:54:23 +08:00
小猫耳朵是什么?
lixuda
2016-11-15 17:35:18 +08:00
都是牛市的时候涨幅。坑

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/320695

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX