模拟北京抄家胜率60%的打炸板首版股票回测代码分享

[复制链接]
查看409 | 回复0 | 2023-10-1 10:24:30 | 显示全部楼层 |阅读模式
北炒是游资中打板一族的一个另类,他总是以打炸板股出名,今天给大家把回测代码也分享了,可以自己去调试优化,如果能卖在早盘10点前高点的80%的话,该策略胜率达到60%,盈亏比是1.54,3%的盈利占比达到26.13%。当然人家不是所有炸板股都买的,有自己的判断,我这里是假设对所有炸板回封股都买的结果。比如北炒在23-8-17日买入重庆啤酒的买点:% v$ R( G3 L; A

% v# t) e' S6 X* b# u/ Q 模拟北京抄家胜率60%的打炸板首版股票回测代码分享-1.jpg
/ ^/ f7 W- u) P这是他在23-8-18的交易记录:- T, E8 d8 ~/ v' A
3 b* ]) b6 M8 y
模拟北京抄家胜率60%的打炸板首版股票回测代码分享-2.jpg - C3 h. S( M3 T: D
这是对所有主板股票回测的代码分享:
2 i  E6 R6 M6 C9 T( M  m1 b   需要注意的是,上述策略的回测过程未计算滑点对策略收益产生的影响。且过去的表现并不能保证未来的表现,市场风险和不确定性永远存在。后面我会根据自己优化后的策略,用5万元开展实盘来验证这个策略的价值,欢迎大家关注,如果持仓标的发生变动会给大家更新。import csv5 d, |# m' A# Y# @9 G9 @2 Q
import os+ y4 ?% ]+ z$ W. b
import time
8 C+ a- r  A+ ?! T8 Q8 O- u! d, {9 B' l* @# B1 d$ j
import backtrader as bt
; B$ J/ k7 H- A) k' N* B5 f0 d& nimport numpy as np+ \& A# k, Y, C2 j2 O, x
import pandas as pd
0 W+ i+ b% H$ {9 A4 O4 e3 f' I# L1 I: Y7 S
from SelfTrade import SelfTrade/ J& @, J$ u1 W9 q0 O; {/ D
import datetime9 c* r! l! g& ^) Z% I; o9 y( f" I

6 P( i% s" c5 M- H* i* o'''炸首板再次打板策略
4 G0 v- r- {  c3 n' Z1. 当日涨停过,又炸板了,再次快要涨停时候买入(可以尝试炸首板,或者近期涨幅在一定范围内的炸板)1 v9 T8 \' T* F; r) \% V
2。第二日早上冲高回落卖出,下跌反弹后卖出,一般10点前挑高点卖出,如果不涨停的话% t5 p5 D: _, r/ Q6 v! J1 T
'''' ~9 T' P9 c% t- K* `6 s2 I4 n' s6 E
class ZhaBanSelect(bt.Strategy):/ T  L1 e$ {5 e! T* o" b) L! h- Z
    def log(self, Type, Code, PriceType, Price, Date):9 p0 H: T8 e' ?% v  O8 \
        ''' 策略的日志函数'''1 \. R( M# \+ F
        with open(self.logFile, 'a', newline='', encoding='utf-8') as file:) W* Y) L6 O! F& `
            writer = csv.writer(file)
. A( Z* k8 v2 t            writer.writerow([Type, Code, PriceType, Price, Date])' P6 @# s9 J% r6 }) k: w9 x
        print('%s, %s, %s, %s, %s' % (Type, Code, PriceType, Price, Date)), M' i' U( \& M1 s9 l; R3 w: `

1 N+ z( H# x; @/ x; o    params = dict(
& @5 H; q3 W# Q. [        hostDays=1,  # 持有最大天数$ I( p, W6 _+ Z
        UP_LIMIT = 0.099,  # 涨停标准. m/ X" f2 j% ^, b+ U) V
        UP_LIMIT_BUY=1.0981,  # 买入起始价位6 E& j' I; n: e( R7 G
        START_TIME='9:30:00',: c) j( z4 s" p2 Z8 E* T8 a* J
        END_TIME='10:00:00',5 C# c5 p6 g4 C1 H! m6 O
        Min_FOLDER=r'/Users/Downloads/tdx/min',
/ G0 T$ k4 U& E  l% B  T/ H& \
+ _( Q( V3 W. ^% l: Q        # 波动低! [8 J8 H0 Q- \' h* H
        LOW_WAVE_RATE=0.3,  # 波动幅度
) H  x( R: |2 P: K        LOW_WAVE_PERIOD=30,
' p# |# [1 d$ L6 ?) j5 ]    )
; b! c- W/ E8 C8 F3 y2 y/ I5 P
    def __init__(self):6 D, R5 o/ Z3 W+ [; w' u
        self.start_dates = {}
9 `0 i8 g; K! m4 e9 f        self.start_dates_flag = {}
, `* X3 D& N9 k# w0 {' Q        self.diff_start_dates_days = {}5 X% N; h" U" A! x0 e  w
        self.selfTrader = SelfTrade(commission_rate=0.0007, cash=10000000)! n9 o3 y& d2 s3 j0 G- ~0 I
        self.candidates1 = {}  # 进入第一波候选观察股池0 M, |% [) O+ @8 k' [' f# P
        self.positionsMy = {}  # 持仓股票及其买入价格5 m1 D$ t7 r: o6 p
        self.days_since_in_host= {}  # 记录每只股票持有后) j1 O9 }% [# l9 V3 b3 w3 G! @7 j
        self.days_since_in_pool= {}  T% G( e1 L$ s; R: z* o/ n
        self.inds = dict()
  }  x  T' V6 @4 I        self.first_day_traded = {}7 v( I- t( ^- q

9 y& N7 P6 b; o# r3 }* J6 u        # 首版7 q! z. e' A% P5 `* C7 ]) ?
        self.candidatesFirstBan = {}  # 进入候选观察股池
  X8 @% W* J4 d/ r. o; s5 t        self.days_since_in_pool_firstBan = {}  # 记录每只股票买入后的交易日数: J, o1 b9 s, D1 b* G

5 U2 @2 M  w+ u+ y+ \# h        # 波动小
3 T. h5 U- o4 l4 j        self.candidatesLowWave = {}  # 进入候选观察股池
! C% k0 D/ R* K& t$ @6 D        self.days_since_in_pool_lowWave = {}  # 记录每只股票买入后的交易日数
2 m2 N; Z5 ]% `7 N" I3 x3 ?8 O. V- U1 ?  ?- u
        # 构建文件名1 z( l' C) Q* n) @* }
        filename = f"首板打炸板持有{self.p.hostDays}天早盘非涨停卖出策略_tdx2023_全部.csv"
( t: |; e! m3 K) `7 j  X        self.logFile = "../out/"+ filename
. ~& U8 h: }, }$ [8 }7 \( |+ Y& T0 X/ T+ X$ m
        with open(self.logFile, 'w', newline='', encoding='utf-8') as file:, m5 V7 l& g5 C
             writer = csv.writer(file)! R4 H% i' Z/ M: O2 H1 s6 |
             writer.writerow(['Type', 'Code', 'PriceType', 'Price', 'Date'])
1 M* C: D" [7 n4 M/ K/ A2 D3 L2 o( D, z' Q/ B, }) }( o
        self.profit_trades = 0  # 交易盈利次数9 l* m5 x3 B7 O7 Z1 u8 S
        self.profit_3percent = 0  # 交易盈利3%比例9 P* k3 w0 J9 v; V* j
        self.stop_loss_trades = 0  # 割肉次数7 u+ H7 C- J3 ?
        self.profit_pct = 0.0  # 盈利时的平均股价涨幅
& N7 O! ~  g, o7 B1 {; n        self.stop_loss_pct = 0.0  # 割肉时的平均股价跌幅! u8 Y1 z  O; w
        self.profit_trades_open = 0  # 交易盈利次数6 B* ]! ?: n% V! x
        self.stop_loss_trades_open = 0  # 割肉次数
1 E7 F. p/ [' n, S# @9 I        self.profit_pct_open = 0.0  # 盈利时的平均股价涨幅
0 r0 G) ?2 ?; s% B8 N        self.stop_loss_pct_open = 0.0  # 割肉时的平均股价跌幅
6 v1 f9 C/ x( M$ A& }        self.count = 0.0  # 统计资金不足次数
; i: Q& W0 G' {' G/ S$ `; |- P# q6 @  ?7 p3 R
        to_remove = []  # 用于存储需要移除的数据对象" k: F  c4 i8 X  l8 _9 {
        for i, d in enumerate(self.datas):& D+ v* e# U( ?6 x' ?* k
            if len(d.array) < self.params.LOW_WAVE_PERIOD:  # 检查数据长度是否足够
) o; t2 W2 ^: w  A5 E                #       print(d._name + &#39;=&#39; + str(len(d.array)))
* I1 Z+ i8 M! U0 }: I                to_remove.append(i)  # 将需要移除的数据对象添加到临时列表8 O% F5 E. c- l. d7 u! f
            else:
$ V6 a+ D6 C; W& M8 e                self.inds[d] = dict(). E) ?4 R! Q7 @) G* \# p5 D
                self.inds[d][&#39;lowesstWave&#39;] = bt.ind.Lowest(d.low, period=self.params.LOW_WAVE_PERIOD)
2 @3 S2 ~2 z# ~! o3 j$ \                self.inds[d][&#39;highestWave&#39;] = bt.ind.Highest(d.high, period=self.params.LOW_WAVE_PERIOD)
7 `, ^% [2 O- ]
# d) o& [% m# L0 X1 S# E1 _- H        # 移除数据对象2 {- o- n1 F# ]* x4 o% J! f
        for i in reversed(to_remove):( {$ C0 j1 s3 w9 J
            self.datas.pop(i)  # 使用pop()方法移除指定索引处的数据对象
  C/ j+ I: `, t% ~. ]4 T6 X
! S4 ]2 o. d5 p4 [7 m        # 检查剩余的数据对象数量& R: i8 O3 {* W$ W
        if len(self.datas) == 0:
& A: ?" r! z* l+ N            print(&#34;所有数据对象已移除,无法运行策略&#34;)  y! q9 k7 l& w  S* P
            self.stop()  # 中止策略的执行6 ^! z5 {! h9 i# v: V7 B" o2 T

6 H  u  x: R& Z! @7 Y. l  E* x    def prenext(self):
2 o. L% P( ~' ~/ `        self.next()
  d6 y+ m% n/ v. y! @. p4 o% n3 `  e
    def notify_trade(self, trade):
5 x# S9 s7 c( R# u0 ~        if trade.isclosed:
- {, x1 k$ B, [( E: W            if trade.pnlcomm > 0:/ j/ ?0 h* T8 |
                self.profit_trades_open += 1
) w+ m3 p  @2 F. p                self.profit_pct_open += trade.pnlcomm+ |( S' R) r" F) }' U& }
            else:
! P* V6 l9 ~, e/ u                self.stop_loss_trades_open += 1
: h# u; l/ u7 J                self.stop_loss_pct_open += trade.pnlcomm
% Z6 v: ~- Y1 K9 ]4 `1 ~
4 p2 D" |& ]3 k! p' l        if self.selfTrader.isclose(symbol= trade.data._name):
0 e: ~4 U& K) a) [! L3 ?            if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] > 0:% B5 n3 x* V% m2 M) X& V) z
                self.profit_trades += 1
9 s  f5 w' b' J1 D4 V7 p                self.profit_pct += self.selfTrader.positions[trade.data._name][&#39;pnl&#39;]; u: n$ ]% ]# q( ]: K5 I
                self.log(&#34;盈利&#34;, trade.data._name, &#34;收益&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))
. y' H2 Y. _: v+ i# }# w# A7 c1 x0 w                if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] >= 300:
+ g2 x5 A: c0 R7 |2 `4 D                    self.profit_3percent += 1& o7 t) A( ]. G# f& F# r6 I6 a
            else:- p6 X0 ^/ B  z3 A# n' {
                if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] > -2500:
2 h! e! W: i0 R$ a; s8 u                    self.stop_loss_trades += 1
$ l, U- F  |+ [% S, p' _8 q                    self.stop_loss_pct += self.selfTrader.positions[trade.data._name][&#39;pnl&#39;]$ R8 N5 j* R5 w- _& U+ v3 g  A
                    self.log(&#34;割肉&#34;, trade.data._name, &#34;损失&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))
5 g; t% B  x- Y5 ?+ i* G                else:
6 \+ T$ e7 }7 T* u6 W( d0 r0 K                    self.log(&#34;可能有除权&#34;, trade.data._name, &#34;问题数据&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))
# z& Y- C& ?6 Z0 v/ M1 Q" d; a# j/ r" V1 S
- Y8 I/ x9 H9 i
    def stop(self):
8 k6 Y9 h2 {0 f* u( U8 Z        if self.profit_trades > 0:
/ P1 I& V8 o# s9 b# b            self.profit_pct /= self.profit_trades
& E7 y. _( ~8 q3 D        if self.stop_loss_trades > 0:" [, x& k5 C! @+ d, i
            self.stop_loss_pct /= self.stop_loss_trades
( c" x4 y) o0 ?1 n3 Q/ a1 N6 `7 }* ~4 {; n/ E; z
        if self.profit_trades_open > 0:
' \% d; D1 o0 F* y            self.profit_pct_open /= self.profit_trades_open" z. W' {: m7 ]1 J1 g- X4 U; h9 r
        if self.stop_loss_trades_open > 0:+ r0 A, G" F; R/ W! y, x! ]; r
            self.stop_loss_pct_open /= self.stop_loss_trades_open
; C# H; o) F7 B, Y        # 打印交易统计信息
/ F6 M( W6 G" j4 E4 M8 Y" ]        print(&#34;交易盈利次数:&#34;, self.profit_trades)/ U0 w5 P  A) _; h
        print(&#34;割肉次数:&#34;, self.stop_loss_trades)5 |0 Z. M; z/ Q7 K% W
        print(&#34;资金不足次数:&#34;, self.count)% O& _4 l! H$ S- o  {/ }* q' X
        print(&#34;盈利时的平均盈利金额: %.2f%%&#34; %self.profit_pct)& T) L/ V5 X1 }9 d
        print(&#34;割肉时的平均亏损金额: %.2f%%&#34; %self.stop_loss_pct)
; u! [! U; J' X6 ]        print(&#39;Final Portfolio Value by Close trade: %.2f&#39; % self.selfTrader.get_total_assets())' S. Q! b" w; }8 d
        if self.profit_trades + self.stop_loss_trades > 0:- g6 b6 Y+ E9 A$ |
            percent = self.profit_3percent / (self.profit_trades + self.stop_loss_trades) * 100
5 s& t% g# s6 j8 Z            print(f&#39;盈利超过3%的比例:   {percent:.2f}%&#39;)8 c" F8 e1 |7 [1 W
        self.log(self.profit_trades, self.stop_loss_trades, self.profit_pct, self.stop_loss_pct, self.selfTrader.get_total_assets())
8 s# D- l1 y) Z  P2 t! M7 D. r5 ?0 b8 f1 o5 t( `8 Z
        current_date = datetime.datetime.now().strftime(&#34;%Y-%m-%d&#34;)1 K+ }. e1 H0 L% ]) G0 Y
        outFile = f&#39;../out/candidates{current_date}.txt&#39;
1 A3 F& L# Q# m2 v% g  X        # 检查文件output.csv是否存在
- l: p" |! b: }; o' q  I  P% |        if not os.path.exists(outFile):2 O3 |/ h, K3 d$ h
            with open(outFile, mode=&#39;w&#39;, newline=&#39;&#39;, encoding=&#39;utf-8&#39;) as file:
+ U% S, i: v5 n6 W                writer = csv.writer(file); E6 \; s) P$ r7 j
                writer.writerow([&#39;代码&#39;, &#39;TargetPrice&#39;, &#39;Type&#39;, &#39;LastClose&#39;])
3 a: {/ h' w7 t4 u6 P. Z6 I1 i+ M' h2 {! p
        with open(outFile, mode=&#39;a&#39;, newline=&#39;&#39;, encoding=&#39;utf-8&#39;) as file:: I- m" ?8 I( o6 G
            writer = csv.writer(file)& z5 K; f2 I# V* R. W" p" `
            for stock in self.candidatesFirstBan:
9 K3 B, T: O  L/ |# D                if stock not in self.positionsMy:
* s; }7 x: F6 z/ P. w5 M                    writer.writerow([stock._name[:-4], round(self.candidatesFirstBan[stock][1] * 1.1 ,2), &#39;炸板&#39;,self.candidatesFirstBan[stock][1]])
1 P* V# W9 ^# `) B2 _) `& R  y3 q* I3 y( p) i* ?
        allStockList = r&#39;../in/all_stocks.csv&#39;
3 e/ X9 m  l6 y# a        df_all = pd.read_csv(allStockList, header=0, na_values=&#39;NA&#39;). ~, A7 v4 H  C, Q5 N6 k
        df = pd.read_csv(outFile, header=0, na_values=&#39;NA&#39;)
* f, @: }2 d; a1 v; }- }9 H2 D6 d- s        merged_df = pd.merge(df, df_all, on=&#39;代码&#39;, how=&#39;left&#39;)  w7 }' ?3 N* U5 b, v
        out_df = merged_df[[&#39;代码&#39;, &#39;TargetPrice&#39;, &#39;Type&#39;, &#39;LastClose&#39;, &#39;名称&#39;, &#39;市盈率&#39;, &#39;概念&#39;]]
8 m+ y" A0 r  y" _/ q9 D        out_df.drop_duplicates(subset=[&#39;代码&#39;], keep=&#39;first&#39;, inplace=True)
; s7 h' L; y6 t        out_df.to_csv(&#39;../out/ZhaBancandidates.csv&#39;, mode=&#39;w&#39;, header=True, index=False): T* X+ T- y  q! H9 V

; M3 Y0 V- O8 S8 k9 z4 ~    def highIntervl(self, data, start_time, end_time):
% C: A+ ^- Y& y' m& u) O: F2 \7 h        stock_name = data._name[2:]  # 获取股票名+ B! M9 @( x' ^. \) j: o$ }
        stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
1 ^2 V8 b5 [! U' _        if os.path.isfile(stock_data_path):, V3 R, p* |; n7 j+ c
            df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;])7 _0 ^% y) d; D  s, R5 ^
            # 设置筛选条件5 H: ~" K( k  ~8 H! b
            next_date = data.datetime.date(0)
3 ?* p& P! \" w/ V& d            next_date_data = df[df[&#39;date&#39;].dt.date == next_date]
2 J% \" b4 e- w* a2 y; ^            while next_date_data.empty:
% S0 `" m! y7 |0 E                next_date += datetime.timedelta(days=1)
, B' Z5 O* r6 u/ k( Z- g3 B                if next_date >= datetime.datetime.now().date():: u: D, @# R& [
                    self.log(&#34;异常:可能停牌&#34;, data._name, &#34;价格&#34;, &#34;&#34;, data.datetime.date(0))
6 K: o$ V2 S' Z" {                    break3 F  }; A1 L2 v; {
                next_date_data = df[df[&#39;date&#39;].dt.date == next_date]4 C" c# s# l# [6 ?
            condition = (next_date_data[&#39;date&#39;].dt.time > pd.to_datetime(start_time).time()) & (next_date_data[&#39;date&#39;].dt.time <= pd.to_datetime(end_time).time())+ `1 b+ m: ~, C5 _$ N
            # 根据条件筛选数据
4 c( O1 h+ W) ]# ]- P) |3 U+ a            filtered_df = next_date_data[condition]% u4 Q/ o5 H. e" g$ r) B
$ v' N; N- H+ J
            # 获取筛选后数据中 &#39;high&#39; 列的最大值* x% s2 N6 @2 ~0 M6 R
            max_high = filtered_df[&#39;high&#39;].max()
: e( d' [5 r7 A- c1 @% w% D            low_min = filtered_df[&#39;low&#39;].min()
3 N6 W; O. m3 O9 X8 H; i4 ]( i3 {: P7 b: h$ ?1 G
            return (max_high, low_min)
5 J6 I+ u! t- k- a# U& Y0 U        else:
/ j' ^" Y" X' Y: f6 p% H            raise f&#34;{stock_data_path} is not exit&#34;
# C, W' X* q$ j4 Z: @, e- r; [: R6 L( S( {
    def LowWaveSelect(self, data):5 `  Y$ V* |* U) b: e
        if not self.upLimit(self.inds[data][&#39;highestWave&#39;][0], self.inds[data][&#39;lowesstWave&#39;][0], self.p.LOW_WAVE_RATE):
! |- z5 A  w$ x5 H3 `4 _            self.candidatesLowWave[data] = data.close[0]  #' I& [+ K5 u1 o1 S
        #        self.log(&#34;添加lowest后选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))3 f2 H$ F! a( o  w

, [' s% T( R6 z0 R' M        # 判断是否需要移除候选观察股池中的股票,在股票池超过stock_day个交易日
/ `1 l. L  P9 R3 Q* S" ^        if data in self.candidatesLowWave:
7 x/ p: ~) U# \2 E            if self.upLimit(self.inds[data][&#39;highestWave&#39;][0], self.inds[data][&#39;lowesstWave&#39;][0], self.p.LOW_WAVE_RATE):
7 D, a9 g" o5 f" Z7 ^. W                self.candidatesLowWave.pop(data, None)
* ~4 j& N; }( z/ W  N  w9 |, ?, c+ R; N; W/ M+ H
    #           self.log(&#34;移除Lowest后选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))' F; q: u3 ]2 U7 M+ p

2 ^( Q/ Z/ d1 j    def FisrtBanSelect(self, data, prev_close):
; J7 k2 Y( w0 ^" C; y        if data in self.candidatesFirstBan:) l3 Y4 i% E1 H5 x
            self.days_since_in_pool_firstBan[data] += 1) p* _% \6 L2 F0 \- v* P
+ l3 c% j) _* {" k" ?
        current_date = self.data.datetime.date()3 o  h7 H0 E. n+ a! ?) H
        if self.upLimit(data.high[0],  prev_close, self.p.UP_LIMIT) and not self.upLimit(data.high[-1],  data.close[-2], self.p.UP_LIMIT):  # 乘以1.0998相当于增长9.98%% p& f4 I- `  h1 g, _6 w
            stock_name = data._name[2:]  # 获取股票名
0 m1 K& m, U8 B  V6 a+ Q            if stock_name not in self.candidatesFirstBan:& S$ p2 b. M$ G! K
                self.candidatesFirstBan[data] = (current_date, prev_close)
' z' E! @" X4 A: b+ R/ T' {1 q                self.days_since_in_pool_firstBan[data] = 0
" @, [' t1 [1 R& Q4 r! ?2 ?! \: h     #           self.log(&#34;添加首版选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))
) y% x+ {( ^, ]! c6 r! J$ O9 E( f* _7 A2 @- R- B/ E0 P
        if data in self.candidatesFirstBan:& _' I  `6 U: Y1 `+ L( Y, e: I' I
            if self.days_since_in_pool_firstBan[data] > 0:
+ t  x( V+ H. @, o                self.candidatesFirstBan.pop(data, None)
% G) {' y5 M# o3 @; o6 }                self.days_since_in_pool_firstBan.pop(data, None)
. e3 C! m' N0 b0 W( O #               self.log(&#34;移除首版选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))( @: O( p4 Z4 Z9 L( u2 q2 S1 H

% ]: m  Y" h( M! M2 q4 H" ~9 B9 a. M8 [3 \
    def upLimit(self, currentPrice, lastClose, upLimit):! O- Y3 m& i/ a+ ~  H
        if currentPrice >= round(upLimit * lastClose + lastClose,2) :( M2 ~3 [" w; X  l2 N: @/ J5 G
            return True- \6 e0 h6 {# S! s1 Y
        else:
6 @3 E. k) |- A5 c7 N5 H4 w            return False' S1 `0 l! h! D$ `# D0 E

, `+ [; ^: y5 [5 q, o0 \' m( Y5 \% _    def downLimit(self, currentPrice, lastClose, downLimit):
* _# F8 H5 p! H) f        if currentPrice - lastClose < downLimit * lastClose:
- D! n* t7 \2 ~; R            return True
" c' s: {8 W  u9 F        else:8 k& l2 E3 A' N! A
            return False
; k: {# u' r! l! y1 I; }
: c5 R/ n' `4 \/ o    def next(self):
. y6 V1 Q0 k$ E/ @. i# C. b# N        for data in self.datas:
& x; E6 I8 ^) v3 P, P% U* k) d            if data not in self.first_day_traded:2 R" E# d4 O! y& h% w+ y7 m
                self.first_day_traded[data] = True
- o6 o, X! J7 X                self.diff_start_dates_days[data] = 0
; F( D  B& M: m) p. t0 j                continue  # 跳过第一天交易判断1 j$ b" ~/ i1 K/ k" R% u
7 p8 y# M5 a) s" J, v- @- h0 e
            if data not in self.start_dates:  j) e0 z9 r$ g
                self.start_dates[data] = data.datetime.date(0), z$ ~* K1 h7 e9 K
                self.start_dates_flag[data] = False+ C9 U& R: e7 C$ ?; k- z$ S) g! |' V
                continue
0 f5 X$ J9 j3 C0 E: e  g6 U            if (data.datetime.date(0) - self.start_dates[data]).days != 0 and  not self.start_dates_flag[data]:
5 \, J) u9 Z3 y$ D% T; ]1 B( {                self.start_dates_flag[data] = True
% v* x- M2 a# }                self.start_dates[data] = data.datetime.date(0)
( V3 N- N. n) a/ l2 A                continue: u9 Q6 t! Q6 {& r- k9 l
            # 当股票当前日大于起始日30天以上才进行计算,保证新股上市30天后才计算," r, F) V7 ?1 v1 d2 }
            if self.start_dates_flag[data] and (data.datetime.date(0) - self.start_dates[data]).days > 30 \
  n0 m5 `1 o4 l% ~( N( l                    and (data.datetime.date(0) - self.start_dates[data]).days > self.diff_start_dates_days[data]:
, S" p* u) t# I9 A" b5 U                self.diff_start_dates_days[data] = (data.datetime.date(0) - self.start_dates[data]).days! l' t5 o" F7 d! ^/ B) g5 x
                #设置前一日价格
  U% Y) y6 E. p- O. E6 j                dividend_flag = False
6 o- `0 Q8 C% D$ [                prev_close = data.close[-1]
- `8 e# Y+ {/ t! g6 i6 R* c                if data.open[0] - prev_close < -0.11 * prev_close and data._name.startswith((&#39;sh60&#39;, &#39;sz00&#39;)) or \
' S( d0 j/ _2 a5 m+ M                        data.open[0] - prev_close < -0.21 * prev_close and data._name.startswith((&#39;sh68&#39;, &#39;sz30&#39;)):6 l% T% o3 d+ p* ~" ^
                    dividend_adjustment = prev_close / data.open[0]
4 A8 z( v. g# }$ r* K: u                    prev_close /= dividend_adjustment( j' X; A; n3 x9 A! o" ]/ l6 E* l/ Y
                    dividend_flag = True
6 Z0 C) b- R  ^- p  \0 `+ j0 l1 X4 u
                self.FisrtBanSelect(data, prev_close)
8 K' e+ v! ?' J5 _                self.LowWaveSelect(data)6 l% ?4 j; y" z- t

1 i6 _) o8 X! Q9 |2 q                # 判断是否需要卖出持仓股票8 B8 B5 b7 G& [$ J$ B
                if data in self.positionsMy:( a: Y2 b+ K; _
                    self.days_since_in_host[data] += 1
0 Y! b6 W7 X3 A                    #除权发生调整成本
# q) |$ \9 _3 }2 z                    if dividend_flag:
3 x* v8 v; z: K3 F- v% }; n                        self.selfTrader.positions[data._name][&#39;price&#39;] /= dividend_adjustment
) [, N( `4 w/ f7 W) C$ K$ m1 R                        self.selfTrader.positions[data._name][&#39;quantity&#39;] *= dividend_adjustment8 L3 X! j) w! C9 v
                        self.log(&#34;除权发生&#34;, data._name, &#34;调整成本价&#34;, self.selfTrader.positions[data._name][&#39;price&#39;], data.datetime.date(0))* l" c7 a# a% @. w4 N0 n

* c& a' W  }! ]- w# f                    stock_name = data._name[2:]  # 获取股票名9 z/ N5 D+ D. ]4 f5 U( @
                    stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
4 C' n4 s3 |6 s                    if os.path.isfile(stock_data_path):
! U: _9 d5 w. _' p1 Z                        df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;], index_col=&#39;date&#39;)4 j: b& v3 ~$ j. U  d+ A& R2 ?
                        next_date = data.datetime.date(0)& t/ f" `+ ?! }$ @1 G  m2 l
                        next_date_data = df[df.index.date == next_date]
5 c. W. S/ S5 \8 N7 x( B                        while next_date_data.empty:- V, f1 a7 |' W" l$ x) @
                            next_date += datetime.timedelta(days=1)
0 O  ]% n+ N! b$ t5 {                            if next_date >= datetime.datetime.now().date():5 }$ m1 S( S' n6 M
                                self.log(&#34;异常:可能停牌&#34;, data._name, &#34;价格&#34;, &#34;&#34;,  data.datetime.date(0))/ v& q3 O. d* j6 z
                                break
# \. J) ?/ n% A8 M! F  b8 ]                            next_date_data = df[df.index.date == next_date]
* _- `7 Y/ }9 Z$ P6 N6 l' a7 |  n. o- _1 \/ j; I; Z! c- H
                        highInterval = self.highIntervl(data, self.p.START_TIME, self.p.END_TIME)$ z5 L" M0 {4 L, q: y# _) o
                        close_price = (highInterval[0] - highInterval[1]) * 0.8 + highInterval[1]  E- p% O* U8 ?$ B

( |0 o' ~7 s( L* X                        if not self.upLimit(close_price, prev_close, self.p.UP_LIMIT):
4 I: I+ N. G" v                            sell_price = close_price* n6 c# K% t0 M0 v' A+ e* L
                            self.log(&#34;早盘卖&#34;, data._name, &#34;价格&#34;, sell_price, next_date.strftime(&#39;%Y-%m-%d&#39;))6 t5 S4 Q- b8 p) ~. r. u
                            self.close(data=data, price=sell_price)
# b& r$ e1 E& J4 ]9 e                            self.selfTrader.close(symbol=data._name, price=sell_price)
. x7 H& q& n% ^# D- y. }. {7 R                            self.positionsMy.pop(data, None)
, M0 M- f1 c3 |) E$ C- T                            # 必须要在卖出股票后移除candidates1股池,否则,卖出当天有可能还会进入买入逻辑,从而导致当天无法完成结算,而且反包规则卖出当天都不符合买入准则/ J- q+ K% `" S8 ^$ `$ F4 G6 t  h
                            self.candidatesFirstBan.pop(data, None)
$ r7 {# O5 ^8 M                        else:/ K  E1 l) ?* |, r5 y3 E
                            self.log(&#34;涨停不卖&#34;, data._name, &#34;涨停价格&#34;, data.close[0], data.datetime.date(0))
+ q' @% N$ k; P4 D
, ?3 k. {2 c* N, W# U5 P5 U4 l- L
. V6 D2 m0 l% y2 j+ F+ w+ H4 \! [                # 判断是否在候选观察股池中,买入炸板股票根据规则  O( p3 @' X. e& u( I) }
                if data in self.candidatesFirstBan and data in self.candidatesLowWave:' |/ Z& E+ s+ N; f" j# h2 O/ i
                    stock_name = data._name[2:]  # 获取股票名
. |( c) J$ _8 ]* F5 F& v                    stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
- o. Y2 m0 c3 f/ P                    if os.path.isfile(stock_data_path):: o# M" ^9 d7 C/ Q' T
                        df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;], index_col=&#39;date&#39;)- t8 Q% x+ D7 F, o  u& ^, G
                        df = df[df.index.date == self.candidatesFirstBan[data][0]]  # 选择日期部分等于current_date的数据% h. L- G; Q$ v, e! U: Y9 F" ~
                        firstUpFlag = True! C* J& x3 h3 N8 V2 c$ N8 q
                        secondUpFlag = True
. J, I; _6 ?! w                        for index, row in df.iterrows():
& V# z7 z/ I  U3 U                            high = row[&#39;high&#39;]
5 j  L! R& R( H( q0 I) }                            low = row[&#39;low&#39;]
6 U7 x& ~# ^: c5 f" U% r                            datevalue = index.time()
" s5 k* I9 f& i2 @" B  P1 Q  K. ~( `% |* o4 ~1 S2 ]1 @& @
                            if self.upLimit(high, prev_close, self.p.UP_LIMIT) and firstUpFlag and secondUpFlag:
! n9 L, t( g8 q  q5 i8 V4 F$ ^                                # 首次涨停4 t. L. B* D% h3 V: c! {) \
                                firstUpFlag = False, P9 B7 W. [/ \3 u( m; o
                                continue
; U7 I, m8 U- B, ?                            if self.upLimit(high, prev_close, self.p.UP_LIMIT) and not firstUpFlag and secondUpFlag and low < high:% |% i5 }8 n7 S& I' M
                                # 打开涨停版
# K6 v8 h# O; q/ V- H6 V: v7 f                                secondUpFlag = False2 G. l& q" [( r1 R
                                continue0 A3 B- [) \' e0 q9 j
                            if not firstUpFlag and self.upLimit(high, prev_close, self.p.UP_LIMIT) and low < high and not secondUpFlag :4 \. g: I; l; n9 Y: \; J  I
                                # 再次准备涨停
  m- R3 w* h) J3 b                                cash = self.broker.getcash()) }! e  Z, v, t4 V; v
                                if cash < 10000:, J: M) |+ S* ~2 n( j' S1 ?& f
                                    self.log(&#34;资金不足&#34;, data._name, &#34;持有股数量&#34;, len(self.positionsMy),' I% M: u- I& v  q" J
                                             data.datetime.date(0))$ X# A2 H$ e! F% e4 ^" b# h. g
                                    self.count = self.count + 1
( L) _# x1 ?4 t5 g. T3 Q                                elif data not in self.positionsMy:+ h' t  n9 G& g2 x+ K  ~4 b+ G
                                    buyPrice = round(prev_close * 1.1 ,2)7 ]8 d1 C( C% n* I5 C+ E4 K4 I& T
                                    sizeOpen = 10000 / buyPrice  |' T2 f5 L' E* G! }2 o8 k& _. @
                                    self.buy(data=data, size=sizeOpen)% l% T! p) `( [/ U
                                    self.positionsMy[data] = buyPrice
8 o. t! \$ X2 R2 _  W, [# O! A                                    self.log(&#34;买入股票&#34;, data._name, &#34;买入价格&#34;, buyPrice, index.strftime(&#39;%Y-%m-%d %H:%M:%S&#39;))0 L! ], k* J8 q
                                    self.days_since_in_host[data] = 1( b9 {3 K9 G/ n) [
                                    sizeClose = 10000 / buyPrice
6 J; h4 c) H  J& Y% t& w                                    self.selfTrader.buy(symbol=data._name, price=buyPrice, quantity=sizeClose)
- ]# R, B2 s; C! N                                break
. N: v) U# I' c7 h0 n/ R" X3 T
2 ?) G: e9 R$ u. _1 A7 F
4 ~6 }, a" |* R& ]now = datetime.datetime.now(), D1 l  O& D1 T0 v& Q# ~9 K
one_day = datetime.timedelta(days=1)- T. u0 P0 F5 y
next_day = now + one_day5 A6 C7 @1 V2 z; d$ x6 B
class MyData(bt.feeds.GenericCSVData):
7 l, w$ j5 l. |; N- {    params = (" T! Y% G& ^! a. u( u2 @1 a: v
        (&#39;dtformat&#39;, &#39;%Y-%m-%d&#39;),
5 G; l0 s* h& i% `1 o% f7 o        (&#39;datetime&#39;, 0),
4 r6 I3 ^* z( @: R/ W        (&#39;open&#39;, 1),
# |: {: i* n3 K! H/ P0 G3 t        (&#39;high&#39;, 2),
1 y" w% E3 Z. |) J        (&#39;low&#39;, 3)," p5 T1 s" B8 b: s) L* y% f
        (&#39;close&#39;, 4)," l9 O5 v3 x1 r& a5 _+ R7 V
        (&#39;volume&#39;, 6),
7 I- b( p! a$ a/ k0 v4 y$ Z4 O; Z        (&#39;openinterest&#39;, -1),
5 Z9 w* x  E% j        (&#39;fromdate&#39;, datetime.datetime(2022, 12, 1)),1 i" f6 D% r5 y; N* j
        (&#39;todate&#39;, next_day),2 t$ {3 h! W! v- p# d7 L4 T
    )9 f' `! g! n* H6 E0 b  n

1 C  S0 c. ~  A0 [  y" W" ~' b  Pif __name__ == &#39;__main__&#39;:
0 y% v$ F- Q0 J4 f" S    time_start = time.time()
) \. O' Y% r/ _3 T: S0 U) f( F    cerebro = bt.Cerebro(stdstats=False, cheat_on_open=False)
6 J  t3 A; e: v; F  t9 Y% H& Z4 ?/ t% v1 F# _5 n: z) d
    # 添加数据源/ |1 c1 T$ f4 O. f8 n
    dataFolder = r&#39;/Users/Downloads/tdx/baostock&#39;4 q  B" f* S, \/ ~, s; A0 l3 E  d
    allStockList = r&#39;../in/all_stocks.csv&#39;. S6 j% |9 d, ?- T# N& k
    df_all = pd.read_csv(allStockList, header=0, na_values=&#39;NA&#39;)" ?4 i% @* t2 M3 `

" K. o" \# v1 {+ u% x7 w    stock_lists = os.listdir(dataFolder)
" P, Y. U& L+ t% D% U3 i3 D% L- c9 ~0 z9 n  C
#    stock_lists = [&#39;sh600132.csv&#39;]; z+ ]9 A: A! R9 W# J( v, r2 \7 i& w
    for stock in stock_lists:
7 {$ V4 e1 h4 ^$ \0 Y* G        name = stock.replace(&#34;.csv&#34;, &#34;&#34;). w6 N! N) \+ I9 @( a
        new_df = df_all[df_all[&#39;代码&#39;] == name]
# _! m2 @3 P6 t+ J/ _) b+ i/ z        if len(new_df) > 0:: L' }; c6 ~2 \  H7 K
            first_row = new_df.iloc[0]% G* b* D+ p1 w4 n) b3 b  b' N
            if &#39;S&#39; not in first_row[&#39;名称&#39;] and &#39;*&#39; not in first_row[&#39;名称&#39;] and &#39;退&#39; not in first_row[&#39;名称&#39;] and &#39;X&#39; not in first_row[&#39;名称&#39;]\7 a& T6 m" {8 \
                    and &#39;D&#39; not in first_row[&#39;名称&#39;] and &#39;C&#39; not in first_row[&#39;名称&#39;] and &#39;U&#39; not in first_row[&#39;名称&#39;] :
" W7 G. [( d, X  Y) i6 X    #              and first_row[&#39;市盈率&#39;] > 0 and first_row[&#39;市盈率&#39;] < 100:
; y2 S- {+ `" L5 U. r                filepath = os.path.join(dataFolder, f&#39;{stock}&#39;)
) U& p0 t& }* W% R' H                size = os.path.getsize(filepath)
! U) v5 g4 U) \$ Y' ~. O. |                if size > 10 * 200:: F4 t, i2 a; S$ u2 [2 T
                    if stock.startswith((&#39;sh60&#39;, &#39;sz00&#39;)):
2 H& e; q7 K$ L5 ]" V/ m8 g0 j. {                        df = pd.read_csv(filepath)
! f5 \  S; B5 U                        df[&#39;date&#39;] = pd.to_datetime(df[&#39;date&#39;], format=&#39;%Y-%m-%d&#39;)
+ L- ^1 ?5 P1 Z7 T. H3 ]2 t                        last_row = df.tail(1)
) F5 I0 D. m' q                        if last_row[&#39;date&#39;].values[0] > np.datetime64(&#39;2023-05-18&#39;):
) z$ y9 R& i8 Q" G                            cerebro.adddata(MyData(dataname=filepath),  name = stock)
$ S5 s4 H$ p. f* N4 w" x
8 ?1 V) M$ E( Z' y" J& @3 H( r    # 添加交易记录1 P& E5 C6 x. M7 y: t! p* `
    cerebro.broker.setcash(10000000.0)
3 B! G2 o3 |) E# e' C    cerebro.broker.setcommission(commission=0.0007)8 `: y+ Z, V. }% x

/ \- }% L% c( e+ X; ~3 ~5 v    cerebro.addstrategy(ZhaBanSelect)
! G/ @- c$ |% K1 D$ u3 Z( n. u/ T, G% a& ^$ R* n' U! [
    cerebro.run()4 {1 l, O5 M8 X  B: c
    time_end = time.time()
0 d, g8 W) [/ |5 W" i1 S7 Z2 b- f: V
    print(&#39;程序所耗时间:&#39;, time_end - time_start)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

11

金钱

0

收听

0

听众
性别
保密

新手上路

金钱
11 元