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

[复制链接]
查看397 | 回复0 | 2023-10-1 10:24:30 | 显示全部楼层 |阅读模式
北炒是游资中打板一族的一个另类,他总是以打炸板股出名,今天给大家把回测代码也分享了,可以自己去调试优化,如果能卖在早盘10点前高点的80%的话,该策略胜率达到60%,盈亏比是1.54,3%的盈利占比达到26.13%。当然人家不是所有炸板股都买的,有自己的判断,我这里是假设对所有炸板回封股都买的结果。比如北炒在23-8-17日买入重庆啤酒的买点:
& o% R( x: Z% S' Q5 }
% Y0 p/ ~$ r$ e% K0 B9 | 模拟北京抄家胜率60%的打炸板首版股票回测代码分享-1.jpg : A, y6 R9 M1 K
这是他在23-8-18的交易记录:8 R4 p* I2 a/ i( ^+ K8 S' e1 f
' i0 q4 A/ Q# T& ?
模拟北京抄家胜率60%的打炸板首版股票回测代码分享-2.jpg
6 _0 J- G$ k( ?* l( I3 }这是对所有主板股票回测的代码分享:
# }  N7 j, [* I- [+ t1 m   需要注意的是,上述策略的回测过程未计算滑点对策略收益产生的影响。且过去的表现并不能保证未来的表现,市场风险和不确定性永远存在。后面我会根据自己优化后的策略,用5万元开展实盘来验证这个策略的价值,欢迎大家关注,如果持仓标的发生变动会给大家更新。import csv
: p/ g; A5 y* ]" N5 x7 A& Zimport os
5 U. i" ~: x) B, B" }( ?import time
; Z7 f" s/ `  [, @0 O* |
; N8 i" N$ l5 ]0 _- _import backtrader as bt4 n, ?% ?* U& c3 C5 }7 S
import numpy as np5 u4 |: Q5 o1 l
import pandas as pd( Q5 R' _# u6 c8 o3 Z( @
/ G$ N. P' G' ~% h0 E# S
from SelfTrade import SelfTrade5 w% {: @6 \5 E3 x
import datetime
. \* H- C: \' g# H7 r$ ^) {6 Y6 w( I! N1 l. A
'''炸首板再次打板策略) ~1 r# g3 s8 X1 {& C6 j
1. 当日涨停过,又炸板了,再次快要涨停时候买入(可以尝试炸首板,或者近期涨幅在一定范围内的炸板)
6 _5 z- U4 l! @' \' V9 @! @2。第二日早上冲高回落卖出,下跌反弹后卖出,一般10点前挑高点卖出,如果不涨停的话- w& K6 h- o/ K+ {& |( Y% k5 S
'''% G% J$ E; ^: M5 d, F
class ZhaBanSelect(bt.Strategy):1 l4 I  X1 s# f( ]6 T' t
    def log(self, Type, Code, PriceType, Price, Date):
* N6 B- p3 I8 ?1 n; P) f- }        ''' 策略的日志函数''', j& s- x* V) V- n, i  D! p
        with open(self.logFile, 'a', newline='', encoding='utf-8') as file:* k: A& S" y- X, \* P
            writer = csv.writer(file): X, k2 V3 ?9 P% ?! r
            writer.writerow([Type, Code, PriceType, Price, Date])/ P" f2 X  j6 m3 C, R$ U- |& S4 }0 |
        print('%s, %s, %s, %s, %s' % (Type, Code, PriceType, Price, Date)), M, [0 c. F  \

4 \0 k+ n) R% d* P9 r    params = dict(
  Q& b" @& F1 Z( s        hostDays=1,  # 持有最大天数& o6 K7 T6 X0 x  m0 x
        UP_LIMIT = 0.099,  # 涨停标准! x6 K  r- P4 w
        UP_LIMIT_BUY=1.0981,  # 买入起始价位
9 ?+ y6 T: ?) a        START_TIME='9:30:00',* C9 h# |; `( ^3 V
        END_TIME='10:00:00',
# g8 B6 q$ S/ r0 X* H$ r! y        Min_FOLDER=r'/Users/Downloads/tdx/min',
0 D! ?  V+ k% a; d+ F- I. a, |0 S7 Q  H
        # 波动低
7 u, e- L4 \* p1 x, i: ?& G* @) Y2 r        LOW_WAVE_RATE=0.3,  # 波动幅度7 u( e: ]  f! v
        LOW_WAVE_PERIOD=30,
( N& ~4 C4 M- N    )% {+ ?  V8 S$ _" M5 b! H

; ~7 d) n) p' O+ n' Q9 [+ \0 U    def __init__(self):
# {/ }$ Y. f4 K2 i. M& X! S        self.start_dates = {}: f) A1 t3 g: v7 M8 l
        self.start_dates_flag = {}  V, }7 t$ @: G+ _) @
        self.diff_start_dates_days = {}
4 F9 e* q! U6 }) {/ Z& h        self.selfTrader = SelfTrade(commission_rate=0.0007, cash=10000000)) g6 c2 X' ]# A9 F
        self.candidates1 = {}  # 进入第一波候选观察股池9 ^9 P5 y* f& z
        self.positionsMy = {}  # 持仓股票及其买入价格
7 a% K% Y5 K5 m# G, }$ ]; T        self.days_since_in_host= {}  # 记录每只股票持有后  A. D- C" ~! O# ]+ P
        self.days_since_in_pool= {}
/ B% r6 Q9 l8 y9 D3 v        self.inds = dict(); v- S$ C" H: z; S! `! l" c
        self.first_day_traded = {}
9 Z2 h# J- T' {8 _+ k* |) x) K1 B4 @7 b
        # 首版
2 X. i( g- K) s+ ?        self.candidatesFirstBan = {}  # 进入候选观察股池
; ]6 c, h* D0 {5 L$ }" I% O        self.days_since_in_pool_firstBan = {}  # 记录每只股票买入后的交易日数
! o; L& ?- X/ c% V* P" M+ p; W. J( {
! |+ n0 Q1 Q: M        # 波动小4 s; f( p) r/ c+ C
        self.candidatesLowWave = {}  # 进入候选观察股池, L: A1 \( Z# r7 ?
        self.days_since_in_pool_lowWave = {}  # 记录每只股票买入后的交易日数
, a# b* k0 {: U. L& Z, A$ j) e2 d8 g
4 S9 j8 V/ q' D* w) S. @        # 构建文件名- P/ |: q: k+ Q0 G
        filename = f"首板打炸板持有{self.p.hostDays}天早盘非涨停卖出策略_tdx2023_全部.csv"4 e) a8 K5 |' L) H1 A; Y' [+ r
        self.logFile = "../out/"+ filename
* I+ W! a3 F  O* C
. j" t4 x5 [4 v; z  W0 ?- j9 G4 k        with open(self.logFile, 'w', newline='', encoding='utf-8') as file:) l+ `* `/ v# B/ v! @8 g8 |  j
             writer = csv.writer(file)
% a6 D# J' T. V) q3 D             writer.writerow(['Type', 'Code', 'PriceType', 'Price', 'Date'])
' E; V& m& ]" C" l( P. A" T$ h1 d
8 y5 N1 i* A+ p1 P4 g; L' B6 E+ P2 r* c9 M        self.profit_trades = 0  # 交易盈利次数, D+ Q+ y- a% h& r' W/ k
        self.profit_3percent = 0  # 交易盈利3%比例
: |+ @7 [# y- b6 v        self.stop_loss_trades = 0  # 割肉次数7 L# M! t* R' I* G) J3 W
        self.profit_pct = 0.0  # 盈利时的平均股价涨幅7 @1 @& z7 A* i. }4 r# C
        self.stop_loss_pct = 0.0  # 割肉时的平均股价跌幅
8 g1 w3 e$ e0 ?# L" x        self.profit_trades_open = 0  # 交易盈利次数! h4 |! p& \4 z1 q5 g8 Z
        self.stop_loss_trades_open = 0  # 割肉次数/ f7 o7 r; v* {
        self.profit_pct_open = 0.0  # 盈利时的平均股价涨幅8 j1 d* I2 J1 O
        self.stop_loss_pct_open = 0.0  # 割肉时的平均股价跌幅4 \) L# Q( T( R+ o1 C
        self.count = 0.0  # 统计资金不足次数8 q& B% ^% s& X

6 n7 {  J. A1 B) b9 b. y3 K1 g2 K        to_remove = []  # 用于存储需要移除的数据对象4 S& p) L. F8 \9 f2 u
        for i, d in enumerate(self.datas):
# J6 ?/ I  h1 M6 Y0 r" Z( X8 p            if len(d.array) < self.params.LOW_WAVE_PERIOD:  # 检查数据长度是否足够$ w& e/ j% {  q8 k- g& @  g
                #       print(d._name + &#39;=&#39; + str(len(d.array)))1 S3 P% |4 n  k) J
                to_remove.append(i)  # 将需要移除的数据对象添加到临时列表: q( t& F! E6 Q- C
            else:# Q7 V# n' E' E
                self.inds[d] = dict()
! m: R4 }, ?" c/ p1 g9 Z                self.inds[d][&#39;lowesstWave&#39;] = bt.ind.Lowest(d.low, period=self.params.LOW_WAVE_PERIOD)* S* A3 w" n- {& d5 }# T9 e3 n
                self.inds[d][&#39;highestWave&#39;] = bt.ind.Highest(d.high, period=self.params.LOW_WAVE_PERIOD)
2 e" P0 M( w9 X. d: ~* [+ d3 d& ]' r1 b1 J, V. e+ Z- ?+ ?
        # 移除数据对象% j- ~+ r* P' {
        for i in reversed(to_remove):
. I3 u: |" J0 s$ p- W2 Q0 f& d            self.datas.pop(i)  # 使用pop()方法移除指定索引处的数据对象# Q: P2 \$ R. K6 p! j5 j3 G$ w1 T
  c1 {' D3 q$ d- a! Q' W9 l' N% ?
        # 检查剩余的数据对象数量
# ?7 M6 I$ u! C" y        if len(self.datas) == 0:
3 D1 |) @: \" {7 a            print(&#34;所有数据对象已移除,无法运行策略&#34;): h9 [8 q  N# e# `+ G8 C4 n
            self.stop()  # 中止策略的执行! M. x# v- m+ X

8 M' _) ^5 z2 q: a8 \  G  ^  R* P    def prenext(self):
6 h2 i3 @7 z3 y        self.next()
3 T6 b8 d6 V0 G: v8 q9 c) l6 w5 E* F- B8 D8 S
    def notify_trade(self, trade):
  i+ I0 w. P6 A0 ?+ ^- a3 ?        if trade.isclosed:0 l2 }, Z. l" H" Q
            if trade.pnlcomm > 0:
: N/ G) J3 d2 |  e6 O5 g- S                self.profit_trades_open += 1/ O3 s1 f5 W, k4 c
                self.profit_pct_open += trade.pnlcomm- t4 |0 N3 R$ C7 |
            else:
& t: z0 f2 n0 P: O                self.stop_loss_trades_open += 1
% M8 p3 l3 t" m2 m                self.stop_loss_pct_open += trade.pnlcomm7 s# K9 M9 Z8 R9 Z% N2 {8 k9 p! m

" _' C3 {3 c; c/ C1 u, `" n        if self.selfTrader.isclose(symbol= trade.data._name):
* m+ C" w/ A; V1 a            if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] > 0:! b# {4 C+ ?- {/ G: Y
                self.profit_trades += 1, K0 Y3 H( k4 [* M. }  R4 w
                self.profit_pct += self.selfTrader.positions[trade.data._name][&#39;pnl&#39;]
* D* ?" F( E  k  p/ d# j9 n; @                self.log(&#34;盈利&#34;, trade.data._name, &#34;收益&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))
# J8 b* b7 S1 e7 R; k5 ~                if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] >= 300:
( v  m& ?$ V7 q                    self.profit_3percent += 1% ~; V" F: B; Y
            else:
4 K8 j1 n. Y# A1 F. S# w                if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] > -2500:
: c/ E6 G& P, e1 g! s6 c' y; p                    self.stop_loss_trades += 1
+ m/ S5 c" y* I7 _; {3 G: @' W                    self.stop_loss_pct += self.selfTrader.positions[trade.data._name][&#39;pnl&#39;]- A! ^- p! c% a$ O
                    self.log(&#34;割肉&#34;, trade.data._name, &#34;损失&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))* T/ {, v8 A; r4 Q! K9 \9 s' q/ `4 q
                else:
+ z3 }9 H/ f) ~0 M/ ]- m( N                    self.log(&#34;可能有除权&#34;, trade.data._name, &#34;问题数据&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))
, j: B; |) y% Y* M! O% a3 n0 \, h; b% m! ~; A& u# {

) _3 S; T: Y  |8 |    def stop(self):
" X9 |8 B1 f6 {/ M% h" z; E2 E        if self.profit_trades > 0:; d% L, E* G5 _. w8 \+ B
            self.profit_pct /= self.profit_trades
2 a  h4 H4 A1 s) W& ~        if self.stop_loss_trades > 0:
: a1 d9 D. {0 m% W            self.stop_loss_pct /= self.stop_loss_trades
% B0 z$ |+ K$ |- E( }& Q8 H+ ]% W$ W) N% y; c
        if self.profit_trades_open > 0:- j% K0 `- n) J/ ^( e
            self.profit_pct_open /= self.profit_trades_open, d* s! o, c! e# h- _8 s
        if self.stop_loss_trades_open > 0:5 E' I+ f0 S2 @1 h4 h* N& P
            self.stop_loss_pct_open /= self.stop_loss_trades_open
% d' ~* H% {5 C# J        # 打印交易统计信息) a3 A/ a* |5 W$ c* `
        print(&#34;交易盈利次数:&#34;, self.profit_trades)  M' y* _! q* B+ u& a
        print(&#34;割肉次数:&#34;, self.stop_loss_trades)
/ c; p# m* v  V2 ]! u" F  ~        print(&#34;资金不足次数:&#34;, self.count)
0 d& ?& E' o. w9 y; @! L        print(&#34;盈利时的平均盈利金额: %.2f%%&#34; %self.profit_pct)
; a5 L7 F/ ?! [0 Q6 g2 a        print(&#34;割肉时的平均亏损金额: %.2f%%&#34; %self.stop_loss_pct)
: b, e  d% M2 H! u! ^5 U        print(&#39;Final Portfolio Value by Close trade: %.2f&#39; % self.selfTrader.get_total_assets()). v! t; d  O/ N- \# B; g
        if self.profit_trades + self.stop_loss_trades > 0:
. c% ]$ {5 v5 N            percent = self.profit_3percent / (self.profit_trades + self.stop_loss_trades) * 100
/ d+ L. w) a' t8 d8 }0 M7 z            print(f&#39;盈利超过3%的比例:   {percent:.2f}%&#39;): t$ b! J: K8 n  s6 N, u" p
        self.log(self.profit_trades, self.stop_loss_trades, self.profit_pct, self.stop_loss_pct, self.selfTrader.get_total_assets())( b( l& g" @+ f) A3 n+ ^1 t( y, t
8 U" @$ x; v* j$ o
        current_date = datetime.datetime.now().strftime(&#34;%Y-%m-%d&#34;)1 {  Z! C, T" p4 I8 u- v7 Y7 k6 M
        outFile = f&#39;../out/candidates{current_date}.txt&#39;
( u4 w& v( L" J' E+ h4 e; d& d, C        # 检查文件output.csv是否存在
1 K; X6 u, s. w& C6 q4 P1 l        if not os.path.exists(outFile):
- J3 ^, u2 a% F            with open(outFile, mode=&#39;w&#39;, newline=&#39;&#39;, encoding=&#39;utf-8&#39;) as file:
3 X* {) m) V% J1 H- P, j* x# Q                writer = csv.writer(file)( ?( \2 R* U  w
                writer.writerow([&#39;代码&#39;, &#39;TargetPrice&#39;, &#39;Type&#39;, &#39;LastClose&#39;])
8 w7 L% W* L; o  y
: A! t6 s/ P9 R        with open(outFile, mode=&#39;a&#39;, newline=&#39;&#39;, encoding=&#39;utf-8&#39;) as file:
. J6 J1 [# ?0 d, T6 y2 Z            writer = csv.writer(file)
/ X# K# a. Z3 v7 `4 J: n4 u            for stock in self.candidatesFirstBan:
9 Y/ l8 t9 O2 J9 F                if stock not in self.positionsMy:
0 I* d) H: [% ~) t9 |                    writer.writerow([stock._name[:-4], round(self.candidatesFirstBan[stock][1] * 1.1 ,2), &#39;炸板&#39;,self.candidatesFirstBan[stock][1]])0 ?& S. g7 X* a
  V9 d; D( c( ?
        allStockList = r&#39;../in/all_stocks.csv&#39;
6 t$ B) m2 k. b        df_all = pd.read_csv(allStockList, header=0, na_values=&#39;NA&#39;)' k, @8 _9 _9 T# x# B$ A) G6 ~
        df = pd.read_csv(outFile, header=0, na_values=&#39;NA&#39;)
8 X) [: F6 W' O8 v+ ]        merged_df = pd.merge(df, df_all, on=&#39;代码&#39;, how=&#39;left&#39;)
5 @# m0 j: Q% y4 o, n2 `        out_df = merged_df[[&#39;代码&#39;, &#39;TargetPrice&#39;, &#39;Type&#39;, &#39;LastClose&#39;, &#39;名称&#39;, &#39;市盈率&#39;, &#39;概念&#39;]]2 d8 o4 ^8 _6 s  C) ]/ E
        out_df.drop_duplicates(subset=[&#39;代码&#39;], keep=&#39;first&#39;, inplace=True)& r) U( _# z' f' ]3 J
        out_df.to_csv(&#39;../out/ZhaBancandidates.csv&#39;, mode=&#39;w&#39;, header=True, index=False)
6 [6 c" @% F" ~2 Q; Z0 m% P1 {& O9 H1 @/ }/ E" u! g
    def highIntervl(self, data, start_time, end_time):) l7 f8 ]! Z3 u) ^* d, p
        stock_name = data._name[2:]  # 获取股票名" u5 O3 b7 d, S7 Z, X& i, p5 K8 L
        stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
  d2 b$ V% h( z7 ]$ O5 c5 O! N1 M- F! v2 x        if os.path.isfile(stock_data_path):7 d% |' p! l; K" p5 [9 n! M$ Z& l
            df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;])# }9 u% h6 O) A' M8 Y- I
            # 设置筛选条件
& H3 |( g7 b1 Z$ f# ]4 e, f: w            next_date = data.datetime.date(0)
# ~' A* A7 d2 ]: M: f5 j/ T9 t            next_date_data = df[df[&#39;date&#39;].dt.date == next_date]
% M! m, h, \# i! I' m            while next_date_data.empty:
( \/ {& X! d; Y6 f4 m) _                next_date += datetime.timedelta(days=1)" f  w/ W, x% v! x
                if next_date >= datetime.datetime.now().date():! R- N+ Z2 G( q1 y# z
                    self.log(&#34;异常:可能停牌&#34;, data._name, &#34;价格&#34;, &#34;&#34;, data.datetime.date(0))
" o. j/ ]) s4 f                    break
0 b" U" B& m9 z# V* X2 n5 j' f& F                next_date_data = df[df[&#39;date&#39;].dt.date == next_date]
# I/ G$ I1 r* e' x5 \            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())
9 J& _  O4 c+ K            # 根据条件筛选数据
( t! Q4 S% g3 F( w  k3 N" v. O            filtered_df = next_date_data[condition]; r9 [1 F% m0 J/ g; a* d' h7 Q& [
% r: \$ N5 ^) J
            # 获取筛选后数据中 &#39;high&#39; 列的最大值7 Y1 M5 T0 [% j" M9 O6 ]
            max_high = filtered_df[&#39;high&#39;].max()6 s0 [4 j" a5 f, ]9 T
            low_min = filtered_df[&#39;low&#39;].min()4 e: s$ F/ s! A- F8 M: Z7 ~

! w0 D/ ^3 }' ~% d0 Y! A' h            return (max_high, low_min)
' p8 w+ }8 U7 r        else:
6 a) J7 l! H' y* }) ~+ e            raise f&#34;{stock_data_path} is not exit&#34;- A, o) E5 Q/ p. y
0 _7 @: b/ h, T6 E0 L/ t
    def LowWaveSelect(self, data):- h5 d$ B6 K- M* O
        if not self.upLimit(self.inds[data][&#39;highestWave&#39;][0], self.inds[data][&#39;lowesstWave&#39;][0], self.p.LOW_WAVE_RATE):
! v& }$ u/ B- Z  C( p, Z% g            self.candidatesLowWave[data] = data.close[0]  #7 ?1 Z+ A* Z0 ?) u$ j
        #        self.log(&#34;添加lowest后选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))) B9 O: P, R# b- @  O3 q& j7 d
/ l  F0 ^; F: ?( [" q
        # 判断是否需要移除候选观察股池中的股票,在股票池超过stock_day个交易日+ y8 }8 v0 G) ~' z5 d8 q7 a
        if data in self.candidatesLowWave:
+ B: ?. m) K# R) L! ~' |            if self.upLimit(self.inds[data][&#39;highestWave&#39;][0], self.inds[data][&#39;lowesstWave&#39;][0], self.p.LOW_WAVE_RATE):/ q" G, G" _, v0 \- t
                self.candidatesLowWave.pop(data, None)
5 B; R* d1 @( N7 P" n7 v* H- c' ~+ D$ a
    #           self.log(&#34;移除Lowest后选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))" M/ h' `) x' v8 a7 I$ I

1 l2 a4 h- w' p' p  X    def FisrtBanSelect(self, data, prev_close):
7 R& V; S, f' H3 I        if data in self.candidatesFirstBan:9 [0 A, Q0 h# r
            self.days_since_in_pool_firstBan[data] += 1$ A: t! }# v8 |3 t$ Y" S3 g# i
& {$ i' C! L1 z0 M" h8 u, i
        current_date = self.data.datetime.date()" L, I* L2 W4 d) Q" s$ L) d) P
        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%3 w, B: d; [2 E/ P
            stock_name = data._name[2:]  # 获取股票名2 f! J' _. V* t5 W" s  B9 u4 M
            if stock_name not in self.candidatesFirstBan:" S3 y* b1 z& V) J% ?4 f
                self.candidatesFirstBan[data] = (current_date, prev_close)/ k9 w% v9 N, }
                self.days_since_in_pool_firstBan[data] = 0) e2 s; M5 A9 o7 G1 E( t$ |
     #           self.log(&#34;添加首版选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))9 j/ M4 G$ S+ k1 j
- X- N$ ~4 j- y+ |# w
        if data in self.candidatesFirstBan:. U; C1 h0 i8 r/ |' A- K) L  \  _
            if self.days_since_in_pool_firstBan[data] > 0:( U2 u: @, g2 |& j1 l! u; U6 @
                self.candidatesFirstBan.pop(data, None)5 \: ]' _0 X8 w  U5 X
                self.days_since_in_pool_firstBan.pop(data, None)
1 r9 v/ ~& u+ T, f- l( Z #               self.log(&#34;移除首版选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))
" n8 T* n. @8 Q9 e. C7 K
9 l4 A& C4 Y" t8 F, }" B3 [- H3 K* z
    def upLimit(self, currentPrice, lastClose, upLimit):
! V" E; I7 E- u7 {( z        if currentPrice >= round(upLimit * lastClose + lastClose,2) :
+ t) d" A1 i+ N" N            return True0 x$ e9 r8 a% j) H8 J
        else:) u0 Y# I  e, n8 P, |- M
            return False
" ?7 ?- g3 a1 o& p9 J$ P* q9 b  ^) ^8 J9 c
    def downLimit(self, currentPrice, lastClose, downLimit):3 ?, q9 P, W0 ?9 d2 C6 U/ k- a
        if currentPrice - lastClose < downLimit * lastClose:& c4 u$ e$ V, h! {' V. r+ c
            return True
; S/ h7 z0 f, u0 a        else:
6 n* g/ U8 ^; U9 I, N4 ^            return False
* T. k; i, o6 Y; @5 R8 i3 U( c+ L* K, z
    def next(self):9 Y4 g5 y0 z+ V+ y+ d0 @0 h1 n
        for data in self.datas:" o. x/ g: o0 O( \0 F9 T
            if data not in self.first_day_traded:
' x( b* K9 k/ B1 ^5 W8 `! J" [                self.first_day_traded[data] = True
) k% p' z9 D: B' }                self.diff_start_dates_days[data] = 0! x, I- j+ e. ?1 K2 W
                continue  # 跳过第一天交易判断2 i/ q% r/ P( }
' O$ G6 L6 ]) s+ W9 m
            if data not in self.start_dates:) G3 i* h% S* d) n
                self.start_dates[data] = data.datetime.date(0)
% M7 W' o7 @& S( P                self.start_dates_flag[data] = False
0 U2 \: ]  {- ?8 P; X                continue9 O5 a2 I& e. ~4 e% u4 J9 Q
            if (data.datetime.date(0) - self.start_dates[data]).days != 0 and  not self.start_dates_flag[data]:
/ _3 k/ z- q8 ~                self.start_dates_flag[data] = True0 l+ f3 b. ^; [) v: X
                self.start_dates[data] = data.datetime.date(0)
) Y1 G; g7 j9 r& o; b1 y                continue8 L: }  U$ V1 A+ u$ d
            # 当股票当前日大于起始日30天以上才进行计算,保证新股上市30天后才计算,
/ _9 }' k, l+ Z9 R: @0 X            if self.start_dates_flag[data] and (data.datetime.date(0) - self.start_dates[data]).days > 30 \8 \4 O4 Z: h1 e
                    and (data.datetime.date(0) - self.start_dates[data]).days > self.diff_start_dates_days[data]:- x/ c- M" N( z2 i4 Q
                self.diff_start_dates_days[data] = (data.datetime.date(0) - self.start_dates[data]).days7 Q6 `! b/ e" d
                #设置前一日价格
2 u. @7 M9 E- O# a4 L0 ^                dividend_flag = False
( P: k2 [5 R3 A' t0 \: C                prev_close = data.close[-1]
4 I1 j# L+ N" \6 }! ]                if data.open[0] - prev_close < -0.11 * prev_close and data._name.startswith((&#39;sh60&#39;, &#39;sz00&#39;)) or \
1 c' p; T" {; ~: h  m8 r7 v& d% L                        data.open[0] - prev_close < -0.21 * prev_close and data._name.startswith((&#39;sh68&#39;, &#39;sz30&#39;)):' F/ }8 U7 S: _
                    dividend_adjustment = prev_close / data.open[0]& M$ `; t! K, E5 x+ N! _
                    prev_close /= dividend_adjustment
3 [7 d; c7 s$ ]" t% F+ x                    dividend_flag = True
# D% u& B/ q+ b% C& R5 F2 M9 D. `* j* J: T
                self.FisrtBanSelect(data, prev_close)
4 _# K/ F* Y) H2 x& F6 o; L                self.LowWaveSelect(data)
! C+ @/ Z% s# p7 ?, e  T1 J. v8 _: A) V
                # 判断是否需要卖出持仓股票
( [, r# D, c2 I                if data in self.positionsMy:
/ x7 g# V. U% X- B, P  X" h+ e* B& P                    self.days_since_in_host[data] += 18 j- X  W. n8 A" w
                    #除权发生调整成本
% B  d+ }) n0 l# C                    if dividend_flag:3 c# Y1 Y8 I9 T1 p% l
                        self.selfTrader.positions[data._name][&#39;price&#39;] /= dividend_adjustment
2 r& n9 ?4 l3 y- }; Q* U) m8 Q# F                        self.selfTrader.positions[data._name][&#39;quantity&#39;] *= dividend_adjustment
, t2 N/ o/ ]4 f6 m                        self.log(&#34;除权发生&#34;, data._name, &#34;调整成本价&#34;, self.selfTrader.positions[data._name][&#39;price&#39;], data.datetime.date(0))3 J5 Q2 e8 x) o5 @
+ G+ C3 ~; e! T" @- p
                    stock_name = data._name[2:]  # 获取股票名
  }0 g0 |+ s) Q- D) e# G9 v                    stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
9 @/ W3 Q; f' y# A, ?5 W                    if os.path.isfile(stock_data_path):# j/ B7 w. ^# k' l' ?
                        df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;], index_col=&#39;date&#39;)3 J$ f$ d& Z7 N0 j
                        next_date = data.datetime.date(0)( Y$ g# L% n: F: f  L
                        next_date_data = df[df.index.date == next_date]' G$ B( s% }% o) T
                        while next_date_data.empty:
0 V9 L2 o7 l' l* N                            next_date += datetime.timedelta(days=1)
4 Q3 `& q; |0 n, k8 c9 {6 N% F8 {                            if next_date >= datetime.datetime.now().date():
  L3 E0 c% O2 b8 V                                self.log(&#34;异常:可能停牌&#34;, data._name, &#34;价格&#34;, &#34;&#34;,  data.datetime.date(0))& A3 ~! h4 s' i
                                break
4 z$ |6 @" z1 l1 j- g- V9 `                            next_date_data = df[df.index.date == next_date]
: ]- x( d3 f. [. c5 g* s1 g' ?9 v
                        highInterval = self.highIntervl(data, self.p.START_TIME, self.p.END_TIME)3 F& D/ K' K, U5 E+ b3 L
                        close_price = (highInterval[0] - highInterval[1]) * 0.8 + highInterval[1]
; ?% o1 i+ n8 L% {: W' i
7 h) L8 o+ @( O5 U4 |1 g2 V                        if not self.upLimit(close_price, prev_close, self.p.UP_LIMIT):
- U+ m; [5 X' H6 u  a. O                            sell_price = close_price
5 w# T, a. ~6 z8 {# W7 h                            self.log(&#34;早盘卖&#34;, data._name, &#34;价格&#34;, sell_price, next_date.strftime(&#39;%Y-%m-%d&#39;)); [' x1 n# g% F/ V3 V' T, x7 w. s
                            self.close(data=data, price=sell_price)
6 t5 z( i5 n. I# ?8 `                            self.selfTrader.close(symbol=data._name, price=sell_price)( c0 v, n% N. s+ N" q' M
                            self.positionsMy.pop(data, None)
' Q: i6 }; w- {0 ?4 I                            # 必须要在卖出股票后移除candidates1股池,否则,卖出当天有可能还会进入买入逻辑,从而导致当天无法完成结算,而且反包规则卖出当天都不符合买入准则
% b9 b% _. h: t                            self.candidatesFirstBan.pop(data, None)
- k$ k/ `4 p* o, x4 F" ^3 f                        else:
7 s8 K! u2 F. E( n                            self.log(&#34;涨停不卖&#34;, data._name, &#34;涨停价格&#34;, data.close[0], data.datetime.date(0))( L( [& ?8 e9 m

  \* V2 w9 x. |$ n8 {( m% b$ t
. D# A4 @. s& t+ O1 E                # 判断是否在候选观察股池中,买入炸板股票根据规则+ A% k) D6 A# \2 ?+ T$ ]0 D+ @
                if data in self.candidatesFirstBan and data in self.candidatesLowWave:7 u. O! j9 e( Q/ {: n$ J' X) Z
                    stock_name = data._name[2:]  # 获取股票名4 X. l2 o- S0 K2 r) Q) S
                    stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)- w  N) `; z: h* n! {" c
                    if os.path.isfile(stock_data_path):: x6 V- S) F/ F( I$ x+ F% [
                        df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;], index_col=&#39;date&#39;)
: a# q  a# r7 Z  E                        df = df[df.index.date == self.candidatesFirstBan[data][0]]  # 选择日期部分等于current_date的数据
# E2 W- X' z( a; q+ ^1 j# M                        firstUpFlag = True
% D. ?1 d& [7 R! u                        secondUpFlag = True
8 z# ]9 S( H9 _+ R- y  {! K                        for index, row in df.iterrows():
+ D+ _' w3 @! |- p) ~; v- ^% ]                            high = row[&#39;high&#39;]" l3 a/ Q9 O6 E0 \1 C
                            low = row[&#39;low&#39;]
* G4 B/ Z" {; x3 [9 n                            datevalue = index.time()! w+ X; F9 @8 I8 D" J

: m* J0 q# m4 p# J5 C                            if self.upLimit(high, prev_close, self.p.UP_LIMIT) and firstUpFlag and secondUpFlag:
* E4 |2 K2 H6 K+ f6 e                                # 首次涨停$ U1 q* A" F) [, E2 L4 C7 V9 J
                                firstUpFlag = False
" ~+ K- i% D) Z1 ~" \' x                                continue
+ f) z5 M6 I2 D; |  W' J& Y                            if self.upLimit(high, prev_close, self.p.UP_LIMIT) and not firstUpFlag and secondUpFlag and low < high:
% [9 ~: R$ B$ ~4 I* H                                # 打开涨停版
' p1 K* ]/ Y' j) G2 x- V                                secondUpFlag = False
  G1 Y& M2 U/ r! I$ o                                continue4 b1 X5 E6 {# I" c( R3 f' y) g
                            if not firstUpFlag and self.upLimit(high, prev_close, self.p.UP_LIMIT) and low < high and not secondUpFlag :
3 D  P& ?4 b' a1 y. e9 p( S                                # 再次准备涨停* g  h  g! n9 k. R% N* \1 {
                                cash = self.broker.getcash(), W8 G6 ~2 ^0 }
                                if cash < 10000:1 S8 z0 Z7 u; G
                                    self.log(&#34;资金不足&#34;, data._name, &#34;持有股数量&#34;, len(self.positionsMy),, `9 }( I+ U) R6 w
                                             data.datetime.date(0))
3 }3 Q0 U' _9 W; q5 X2 o                                    self.count = self.count + 1
0 i! k1 d7 C' ^7 q                                elif data not in self.positionsMy:1 r, U" w6 T- b- H* q3 N$ |. r
                                    buyPrice = round(prev_close * 1.1 ,2)% D$ y+ P& |. k! w7 e1 {! A5 j
                                    sizeOpen = 10000 / buyPrice9 r" o- {% g' Q  C3 o. p0 G
                                    self.buy(data=data, size=sizeOpen)
  [: g' C* W( T( I+ Q                                    self.positionsMy[data] = buyPrice$ P1 n; D2 Z- z  b& v5 E# B
                                    self.log(&#34;买入股票&#34;, data._name, &#34;买入价格&#34;, buyPrice, index.strftime(&#39;%Y-%m-%d %H:%M:%S&#39;))
# {7 V$ d' |3 k                                    self.days_since_in_host[data] = 1; u2 ~2 x( j* N+ D% o  F
                                    sizeClose = 10000 / buyPrice
5 \: W* }6 `" [                                    self.selfTrader.buy(symbol=data._name, price=buyPrice, quantity=sizeClose)% _) C0 V9 `8 t: o
                                break
* K0 E; K: `& ]( S
! ]3 T+ S' m: A" {' o! _
" r4 U  h# u# a4 N) {now = datetime.datetime.now()
# J8 E5 B' l7 e9 |3 ?* Yone_day = datetime.timedelta(days=1)' g+ W$ m9 a5 S/ z% @
next_day = now + one_day3 w$ U2 U1 m7 l; {8 d
class MyData(bt.feeds.GenericCSVData):2 z( z. l1 s' _
    params = (1 w! F7 g) d/ y, ^8 k
        (&#39;dtformat&#39;, &#39;%Y-%m-%d&#39;),' x0 m: A0 v# Q# \. f* t: x9 k
        (&#39;datetime&#39;, 0),
5 i+ {$ U3 ]9 H1 s8 J7 g' ]( D        (&#39;open&#39;, 1),9 L5 I8 w1 v+ D* E4 z
        (&#39;high&#39;, 2),) `4 T  d5 Y& {! U$ u! _
        (&#39;low&#39;, 3),2 @+ ~" I7 Q& |/ V% F0 w; C3 X
        (&#39;close&#39;, 4),
# v" s" g( U* `0 [6 J  Z, ~        (&#39;volume&#39;, 6),0 w' q; k& }$ w' s
        (&#39;openinterest&#39;, -1),
) A: Z$ X, P, n; J% e. k8 [2 l        (&#39;fromdate&#39;, datetime.datetime(2022, 12, 1)),
* H5 m/ |  S; h6 T) c        (&#39;todate&#39;, next_day),( x4 w* S$ r# b0 L; d0 G" d4 H
    )( f3 M4 t) E, @7 i
" y' ~) t+ }. |& B7 z. L
if __name__ == &#39;__main__&#39;:
  P) Y! z8 M& F" A7 O    time_start = time.time(); G( ^- g5 i) `1 X% s2 [
    cerebro = bt.Cerebro(stdstats=False, cheat_on_open=False)
% K8 u+ @( c/ T& S! z3 k  j. V/ f, S
    # 添加数据源
* O5 V+ S3 [1 U5 q7 m) d/ j5 G# t) f    dataFolder = r&#39;/Users/Downloads/tdx/baostock&#39;0 x) v' p- y2 Z/ ?4 ]3 t4 x" z
    allStockList = r&#39;../in/all_stocks.csv&#39;6 z+ N0 I6 k1 |- P5 X; L) z* _
    df_all = pd.read_csv(allStockList, header=0, na_values=&#39;NA&#39;)2 Q" J/ C+ F  b& \. o

: P) x* l2 w; ~3 q    stock_lists = os.listdir(dataFolder)* \* R+ y3 P3 {+ a7 I4 x7 m

2 O* H6 d* W! s9 M7 \; T, s#    stock_lists = [&#39;sh600132.csv&#39;]/ E7 t- m0 L" O( E/ I" k
    for stock in stock_lists:
* S$ H0 ?9 f0 N& \8 ]1 ?( k        name = stock.replace(&#34;.csv&#34;, &#34;&#34;)6 X) J  V" _, q/ H' e
        new_df = df_all[df_all[&#39;代码&#39;] == name]1 P( v7 ^! ?; b- J4 t- i- ?
        if len(new_df) > 0:0 K" x. C& P) b8 y0 C
            first_row = new_df.iloc[0]5 P; ]2 C+ ~$ i- P5 k( `
            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;]\& ]! E% ]/ D6 k3 i1 z! [: ^7 l
                    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;] :/ _2 ]" {5 X% `  ^& U
    #              and first_row[&#39;市盈率&#39;] > 0 and first_row[&#39;市盈率&#39;] < 100:, s' I- A( P" a- c& y; H' J0 O: n
                filepath = os.path.join(dataFolder, f&#39;{stock}&#39;)! r# G/ h. p8 D' b
                size = os.path.getsize(filepath)
2 ^  H, l7 r. O$ R                if size > 10 * 200:
. d* n$ `. V" Q6 @7 X                    if stock.startswith((&#39;sh60&#39;, &#39;sz00&#39;)):
: M0 @2 d4 V0 j% X! C# b+ d/ m                        df = pd.read_csv(filepath)
& Z& q* ?4 D( e5 ?1 b                        df[&#39;date&#39;] = pd.to_datetime(df[&#39;date&#39;], format=&#39;%Y-%m-%d&#39;)& c  D& Q1 o2 k  P
                        last_row = df.tail(1)
- C6 U$ F  r& z4 f+ J* N                        if last_row[&#39;date&#39;].values[0] > np.datetime64(&#39;2023-05-18&#39;):
- F7 Q$ n0 E% Z3 J: o; I5 f                            cerebro.adddata(MyData(dataname=filepath),  name = stock)* [8 U1 M  U+ }) w

  X: X: B# K3 B1 ^9 a" f0 ^' Q) T    # 添加交易记录$ V% R9 n( C* Z( h, Y4 M4 ]8 F
    cerebro.broker.setcash(10000000.0)
7 r$ }, M. E2 e    cerebro.broker.setcommission(commission=0.0007)0 |, Q' t# ?0 ^4 m

. t3 t! F6 m( M9 Q    cerebro.addstrategy(ZhaBanSelect)
$ k  _: g- j( b7 C( F, |5 j) S/ }
9 \4 \! ^8 p" z# l* ]    cerebro.run()
1 }. I+ m0 \2 z6 d    time_end = time.time()
# a0 C  D, v' `4 x8 b' S; Y, V) ^7 X  [$ A: h: E2 V1 W
    print(&#39;程序所耗时间:&#39;, time_end - time_start)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

11

金钱

0

收听

0

听众
性别
保密

新手上路

金钱
11 元