V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
thinkingmind
V2EX  ›  推广

海龟交易系统的 Python 完全版

  •  
  •   thinkingmind · 2016-11-15 16:35:53 +08:00 · 1155 次点击
    这是一个创建于 2929 天前的主题,其中的信息可能已经有所发展或是发生改变。

    **新鲜出品:

    新鲜出品: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?

    2 条回复    2016-11-15 17:35:18 +08:00
    rust
        1
    rust  
       2016-11-15 16:54:23 +08:00
    小猫耳朵是什么?
    lixuda
        2
    lixuda  
       2016-11-15 17:35:18 +08:00
    都是牛市的时候涨幅。坑
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2805 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 07:35 · PVG 15:35 · LAX 23:35 · JFK 02:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.