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

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

* `  c3 r+ f. R$ E+ a6 Q( I# n 模拟北京抄家胜率60%的打炸板首版股票回测代码分享-1.jpg
( k3 U# O, l4 A( c- i这是他在23-8-18的交易记录:6 E) J. j& A1 l1 o0 k: O  T+ `0 r

2 O& Q  S- A3 }& S' m( I" l' P$ p) |2 a 模拟北京抄家胜率60%的打炸板首版股票回测代码分享-2.jpg
, O" E$ \/ u- E+ B7 m这是对所有主板股票回测的代码分享:
4 K$ P! W* N% S/ j3 }  d# b   需要注意的是,上述策略的回测过程未计算滑点对策略收益产生的影响。且过去的表现并不能保证未来的表现,市场风险和不确定性永远存在。后面我会根据自己优化后的策略,用5万元开展实盘来验证这个策略的价值,欢迎大家关注,如果持仓标的发生变动会给大家更新。import csv" [+ k! u3 ]1 I" c' V2 T2 s
import os, ^3 l! @1 `' i
import time3 ]  j) E: ?% y( D7 X& E
6 B6 o7 @; k9 M& V0 z) h+ l7 `8 P
import backtrader as bt
( o; c7 _0 r; Z. B7 Iimport numpy as np( M. Y3 s& c8 m
import pandas as pd
( r9 @2 \& j) {7 W/ b9 N8 T( t  {
% Y) ]  t4 k8 o( kfrom SelfTrade import SelfTrade
% i/ m8 V7 n) h; v$ F0 x2 Iimport datetime5 D2 n/ C1 z  k: v$ f

: J& D* ^2 b( K/ P'''炸首板再次打板策略
* W! z$ z3 K5 ]# @5 r/ a1. 当日涨停过,又炸板了,再次快要涨停时候买入(可以尝试炸首板,或者近期涨幅在一定范围内的炸板)
# Y4 `( a0 n( l* @# P2。第二日早上冲高回落卖出,下跌反弹后卖出,一般10点前挑高点卖出,如果不涨停的话: u! o; u5 ~1 V% D2 }5 [
'''# {2 v9 M# V: b# }9 }
class ZhaBanSelect(bt.Strategy):2 x: K) k- B1 T! z0 k2 P$ c
    def log(self, Type, Code, PriceType, Price, Date):
9 z2 b3 z6 h% h6 s0 C        ''' 策略的日志函数'''
+ F0 I  L  K/ s# X4 I        with open(self.logFile, 'a', newline='', encoding='utf-8') as file:% f$ h$ b4 \& |- G) T
            writer = csv.writer(file)
# O7 E" u% L$ g: K% J            writer.writerow([Type, Code, PriceType, Price, Date])
6 Z! d  J6 [4 C7 j5 U5 G; m1 h        print('%s, %s, %s, %s, %s' % (Type, Code, PriceType, Price, Date)), q: ?# Z! ^+ P' O4 Y# q  d
3 e$ ^! ^7 B8 i; W! F+ h( k( R
    params = dict(
* A. }" A8 q6 X. ?1 J1 \        hostDays=1,  # 持有最大天数
. S$ `! B' X, m' ]) Z        UP_LIMIT = 0.099,  # 涨停标准
. C! V2 }$ J, L- y' I        UP_LIMIT_BUY=1.0981,  # 买入起始价位
4 K; i% A6 F) y  e) g        START_TIME='9:30:00',
/ p8 |( X6 t3 _, p, E; M        END_TIME='10:00:00',
: q0 W( W) S  z0 Y        Min_FOLDER=r'/Users/Downloads/tdx/min',. ~+ T' i5 Q; S; G* j% {

- s5 J+ }0 P. u# D0 U; H        # 波动低
+ f% D/ p$ s( [/ S        LOW_WAVE_RATE=0.3,  # 波动幅度
+ i  v6 v3 E) G$ g: u        LOW_WAVE_PERIOD=30,
* V4 \# O, I& F4 x    )
3 j0 S! g# z+ \$ P* k9 p
# P2 z+ n- f; E2 H9 g: U6 p8 A3 X    def __init__(self):+ c( a1 p  i3 l4 F4 N8 Q
        self.start_dates = {}9 M+ z1 `/ |5 r1 P5 Y
        self.start_dates_flag = {}) v" l+ b# o% {& X$ J
        self.diff_start_dates_days = {}4 x. P% s. r$ Q
        self.selfTrader = SelfTrade(commission_rate=0.0007, cash=10000000)0 f  k+ L7 y% Q" e0 C. R; J, e
        self.candidates1 = {}  # 进入第一波候选观察股池
6 N5 A0 Z- ^) {5 o7 _. e        self.positionsMy = {}  # 持仓股票及其买入价格
7 j+ r: T. k1 x2 ]3 l        self.days_since_in_host= {}  # 记录每只股票持有后* K( b' ~; c9 p! S0 W
        self.days_since_in_pool= {}9 P) j2 K7 a2 l$ [
        self.inds = dict()& a. _  N: P3 U  u& u
        self.first_day_traded = {}
7 k0 x+ y8 O2 Q4 l- d! j! m+ X: G" v9 l1 M1 X3 X
        # 首版4 d! ?& O2 D9 O7 X% z. p5 s7 z9 z
        self.candidatesFirstBan = {}  # 进入候选观察股池4 H1 _4 h/ c3 q7 @7 S6 f
        self.days_since_in_pool_firstBan = {}  # 记录每只股票买入后的交易日数' @% u& ]% t' P' F' C$ m5 q5 R

' [! d" a8 U; l7 g        # 波动小: u2 Z0 Y1 V% s9 V
        self.candidatesLowWave = {}  # 进入候选观察股池
( b3 _& ]  q/ J6 x        self.days_since_in_pool_lowWave = {}  # 记录每只股票买入后的交易日数7 Q# ^/ l2 t; a  g6 u( f5 G0 R
7 ~. `4 L1 ]$ p
        # 构建文件名- B' c& X9 s+ _, k( T, A6 s. Z
        filename = f"首板打炸板持有{self.p.hostDays}天早盘非涨停卖出策略_tdx2023_全部.csv"$ L; M% V- g, k1 L
        self.logFile = "../out/"+ filename
- Y: C( e  O; i
$ z9 v0 d% Q  J& M( Q- E9 P7 T8 c        with open(self.logFile, 'w', newline='', encoding='utf-8') as file:
. c( n7 P4 H! r  W$ {             writer = csv.writer(file)
* V! m7 _) i* X% G& x             writer.writerow(['Type', 'Code', 'PriceType', 'Price', 'Date']): w* f6 w  S. D% u. ~! z2 T

* z) x& j( n1 h  }6 `, f% o        self.profit_trades = 0  # 交易盈利次数
  _: k% H" I4 H9 p7 C        self.profit_3percent = 0  # 交易盈利3%比例
5 s8 U: Z- C$ r- S        self.stop_loss_trades = 0  # 割肉次数
4 R, a3 X% ~% l9 Y) K* G        self.profit_pct = 0.0  # 盈利时的平均股价涨幅
2 j9 M. t/ v! a: j/ K3 \' u        self.stop_loss_pct = 0.0  # 割肉时的平均股价跌幅1 f. k. d3 s* P/ C5 K; S
        self.profit_trades_open = 0  # 交易盈利次数
! O" m8 e0 L$ N! Z: h. w8 j        self.stop_loss_trades_open = 0  # 割肉次数
/ N1 J# j" R- ~0 R; O, l        self.profit_pct_open = 0.0  # 盈利时的平均股价涨幅
8 s, S6 l" t9 U. c1 m        self.stop_loss_pct_open = 0.0  # 割肉时的平均股价跌幅
0 g- x5 a9 }( h: f        self.count = 0.0  # 统计资金不足次数3 f$ w7 R$ m# _, U1 i% z

$ d8 P% c4 E6 O; P% y: o        to_remove = []  # 用于存储需要移除的数据对象9 q6 b" Z9 O" O2 }9 ~9 z
        for i, d in enumerate(self.datas):/ M/ {1 w9 V/ t' d0 ^6 o3 d; h
            if len(d.array) < self.params.LOW_WAVE_PERIOD:  # 检查数据长度是否足够$ R. n& U/ N" b- e" n
                #       print(d._name + &#39;=&#39; + str(len(d.array))), V* A+ D4 I2 |3 @- T- O$ X
                to_remove.append(i)  # 将需要移除的数据对象添加到临时列表% k8 u) z$ N, K; \4 C! N4 p, r0 R
            else:
" ^3 z( @5 q' L. f( [1 F                self.inds[d] = dict()
. U# M7 U( H8 p, H0 W& A4 |/ a  C                self.inds[d][&#39;lowesstWave&#39;] = bt.ind.Lowest(d.low, period=self.params.LOW_WAVE_PERIOD)
/ B# X8 [7 h. j  B                self.inds[d][&#39;highestWave&#39;] = bt.ind.Highest(d.high, period=self.params.LOW_WAVE_PERIOD), `9 R3 [* r" a+ V% K
, f  m( @, u1 F* ~/ C
        # 移除数据对象5 y; }/ y; @8 @
        for i in reversed(to_remove):! g, J' y8 P" a& }% O
            self.datas.pop(i)  # 使用pop()方法移除指定索引处的数据对象
2 d- u/ O, V! [& l6 ^8 u- W& K
- ]8 t. f. {% M( G* R6 t        # 检查剩余的数据对象数量
8 [# ^' f: c" c4 U( U. o7 D2 K+ Y        if len(self.datas) == 0:" P- ?1 y) ~  p( V& L
            print(&#34;所有数据对象已移除,无法运行策略&#34;)
9 R. t. {$ w, b& j* E& c# S/ p            self.stop()  # 中止策略的执行
  W6 _' T+ q+ r' `3 J2 k- @1 U, |8 l2 k+ [! ~/ k3 L  i
    def prenext(self):
- J/ b; R! s* w0 {* V/ y" M        self.next()  Q) K6 K- [4 g3 P0 z

1 ]9 ^0 @: n! i& `: L# k- c    def notify_trade(self, trade):
& A9 {- \& ]: D$ A% e        if trade.isclosed:8 e3 G- s/ ]! C
            if trade.pnlcomm > 0:) v! u- i5 s1 b6 M* u/ c: c
                self.profit_trades_open += 1
  V5 i& z  H* [                self.profit_pct_open += trade.pnlcomm
9 d2 D/ W  l# F1 S: e            else:
$ @; Q6 ~- c& f; N; l                self.stop_loss_trades_open += 13 d# x6 _9 p8 ^) c7 i7 _# |
                self.stop_loss_pct_open += trade.pnlcomm
. G  P) C  Z7 {! Z# i3 E7 \, T1 K
% E; W. @- l9 c, Z6 ^$ G        if self.selfTrader.isclose(symbol= trade.data._name):
- v3 @- ~1 o- P7 S, p% Z; F            if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] > 0:
" w& y0 A/ D7 p8 |  T2 Z                self.profit_trades += 1+ g' f* l, R% |5 r5 C5 m3 y0 D; p
                self.profit_pct += self.selfTrader.positions[trade.data._name][&#39;pnl&#39;]
. |0 X( ^4 k1 I4 g) R6 ~                self.log(&#34;盈利&#34;, trade.data._name, &#34;收益&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))
: P" o0 N3 a  M) c2 W, g6 s% c* Z                if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] >= 300:/ t/ t* k! h  _  E$ A: p2 W* B
                    self.profit_3percent += 17 A' Y# ]% I( {; j# P
            else:
! [: E6 J2 \( N; @- h3 a                if self.selfTrader.positions[trade.data._name][&#39;pnl&#39;] > -2500:/ {2 J% W* \% p& v* I4 D
                    self.stop_loss_trades += 1: O+ P. |# K' s# ]
                    self.stop_loss_pct += self.selfTrader.positions[trade.data._name][&#39;pnl&#39;]
4 Q/ V2 ]6 e7 R7 b                    self.log(&#34;割肉&#34;, trade.data._name, &#34;损失&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))" k9 i' o2 l2 T% H0 K4 O7 V. a& D: Y
                else:
, ~' f  A  O" i# J! o$ F9 O                    self.log(&#34;可能有除权&#34;, trade.data._name, &#34;问题数据&#34;, self.selfTrader.positions[trade.data._name][&#39;pnl&#39;], trade.data.datetime.date(-1))* O+ w* O$ t8 X3 ^) v% f; T% Q4 h0 _# ?

# z1 @" t5 H* E9 C* C8 D$ E4 Y! C7 `$ ^7 C( H7 l& [1 @
    def stop(self):
3 b; F8 g' W8 V. I$ \        if self.profit_trades > 0:) z8 h1 j% i$ b+ y
            self.profit_pct /= self.profit_trades2 e0 q! {$ i8 Y7 A+ |
        if self.stop_loss_trades > 0:
% M; p$ X& q, Q( {0 o            self.stop_loss_pct /= self.stop_loss_trades
: F' f( m5 i" I% D& F# i1 @/ A  q0 T% T
        if self.profit_trades_open > 0:! v$ A' w$ d# b- x! H) Q9 g" [
            self.profit_pct_open /= self.profit_trades_open: D" X) B# _6 D
        if self.stop_loss_trades_open > 0:; I/ v* p" l, E
            self.stop_loss_pct_open /= self.stop_loss_trades_open, @! H) ?" G% E$ G0 c5 k) X
        # 打印交易统计信息
4 l1 [' C3 [2 a; {9 y) x0 t        print(&#34;交易盈利次数:&#34;, self.profit_trades)
' H7 T! ?" v0 m/ w6 x        print(&#34;割肉次数:&#34;, self.stop_loss_trades)0 g8 O9 G! b3 z( m+ m0 f
        print(&#34;资金不足次数:&#34;, self.count)
+ j; a* _$ O9 y8 U) i        print(&#34;盈利时的平均盈利金额: %.2f%%&#34; %self.profit_pct)
( k. D- ^' n7 S) F+ a7 [+ o        print(&#34;割肉时的平均亏损金额: %.2f%%&#34; %self.stop_loss_pct)8 Y: K2 g& ^6 c1 I
        print(&#39;Final Portfolio Value by Close trade: %.2f&#39; % self.selfTrader.get_total_assets())' O, G; K& W5 D' d! N' I" k  k0 [
        if self.profit_trades + self.stop_loss_trades > 0:
" f/ }: K! k8 A, v: g2 X            percent = self.profit_3percent / (self.profit_trades + self.stop_loss_trades) * 100
% e( S9 ]' V9 x. U; W0 I! M# g4 i4 p4 a            print(f&#39;盈利超过3%的比例:   {percent:.2f}%&#39;)
! S9 y4 t7 ]+ w( |        self.log(self.profit_trades, self.stop_loss_trades, self.profit_pct, self.stop_loss_pct, self.selfTrader.get_total_assets())! f8 B& ~* g0 h# \$ Q
0 K% l+ V7 P4 W/ i) z; k
        current_date = datetime.datetime.now().strftime(&#34;%Y-%m-%d&#34;)& M& |5 d+ `$ a; ?1 r5 A4 v7 ~
        outFile = f&#39;../out/candidates{current_date}.txt&#39;
" M5 U/ B- @0 f- m        # 检查文件output.csv是否存在9 ~, P/ `& [: {! Y$ E& L* {
        if not os.path.exists(outFile):
" u' n, E0 l$ k# `, u            with open(outFile, mode=&#39;w&#39;, newline=&#39;&#39;, encoding=&#39;utf-8&#39;) as file:
; i: s* X. N- `+ V+ m2 g3 O                writer = csv.writer(file)
- c% _" ?/ [: D$ `2 z) v                writer.writerow([&#39;代码&#39;, &#39;TargetPrice&#39;, &#39;Type&#39;, &#39;LastClose&#39;])
; L. _* u3 w- \  L
2 v2 E# s. [1 J& x: z5 X        with open(outFile, mode=&#39;a&#39;, newline=&#39;&#39;, encoding=&#39;utf-8&#39;) as file:
9 h0 E" v) E7 P3 G  v            writer = csv.writer(file)
' ^9 I4 E1 |$ [            for stock in self.candidatesFirstBan:* z8 z! z' {5 |/ ^1 W
                if stock not in self.positionsMy:
& v) d$ w! M  }4 w6 X3 Q0 u                    writer.writerow([stock._name[:-4], round(self.candidatesFirstBan[stock][1] * 1.1 ,2), &#39;炸板&#39;,self.candidatesFirstBan[stock][1]])' {% [, `+ _% z# \- M, E
  G, F  O( W* j) z
        allStockList = r&#39;../in/all_stocks.csv&#39;
" _" j2 S6 s. [1 d& n5 J        df_all = pd.read_csv(allStockList, header=0, na_values=&#39;NA&#39;): }1 V/ _. _0 X2 ]8 Z
        df = pd.read_csv(outFile, header=0, na_values=&#39;NA&#39;)
3 |! ?+ S; x+ _1 _        merged_df = pd.merge(df, df_all, on=&#39;代码&#39;, how=&#39;left&#39;)& S# z. P  S. i
        out_df = merged_df[[&#39;代码&#39;, &#39;TargetPrice&#39;, &#39;Type&#39;, &#39;LastClose&#39;, &#39;名称&#39;, &#39;市盈率&#39;, &#39;概念&#39;]]+ U$ Z& T: z: {- w8 J& i  U$ f
        out_df.drop_duplicates(subset=[&#39;代码&#39;], keep=&#39;first&#39;, inplace=True)
  }  F  x$ M* A# P+ O" ~" ~7 d6 \        out_df.to_csv(&#39;../out/ZhaBancandidates.csv&#39;, mode=&#39;w&#39;, header=True, index=False)
3 T- k" `  z# ~8 s$ w, f
$ m5 A9 K3 R* |    def highIntervl(self, data, start_time, end_time):7 @/ R$ B' y' O5 Q, E- J+ l  y, w
        stock_name = data._name[2:]  # 获取股票名
9 V2 U, ^3 u+ u6 X( I: x4 s; g        stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
) W' ~2 U' @. [; v, F; ]  r        if os.path.isfile(stock_data_path):
- s+ s2 G. q% t' ^$ j; ]1 J            df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;])# Q9 z6 N* D5 G2 c7 ?
            # 设置筛选条件  g' V! ~2 s( @! w/ Y
            next_date = data.datetime.date(0)
) E( U) z% i' c            next_date_data = df[df[&#39;date&#39;].dt.date == next_date]
: ?0 q% ^& u0 }6 I) p            while next_date_data.empty:7 Z. W8 X, |3 i6 G4 Z
                next_date += datetime.timedelta(days=1)
& F. \& m1 A( U% J                if next_date >= datetime.datetime.now().date():3 e/ e1 f+ Z8 n% w
                    self.log(&#34;异常:可能停牌&#34;, data._name, &#34;价格&#34;, &#34;&#34;, data.datetime.date(0))
) a. J: Y/ E" W6 w                    break
# J, B; r- W5 a1 _. O0 V, a3 T                next_date_data = df[df[&#39;date&#39;].dt.date == next_date]4 W! ~. E* N( j
            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())
  ^6 S% J3 I( [+ R/ z. S9 j            # 根据条件筛选数据9 ~  W9 i9 c! ^9 D, L4 Q
            filtered_df = next_date_data[condition]
5 ~5 T3 J+ P4 }' ?0 m! L4 P
. v$ ^4 ]' C+ v3 X: h+ ~5 v            # 获取筛选后数据中 &#39;high&#39; 列的最大值
' z7 c. J# a- b! N) V- w1 [            max_high = filtered_df[&#39;high&#39;].max()' \. K3 X9 v5 L- I
            low_min = filtered_df[&#39;low&#39;].min()* ?, }- s% w; _) d
- b  r+ Q6 a# {0 |  X
            return (max_high, low_min)
, ]2 [% k4 C. C6 E7 H( v        else:- S7 a; Z- l" g6 [- X: B9 }
            raise f&#34;{stock_data_path} is not exit&#34;
, g% Q: K% [8 a& t) k+ ^" u9 q
+ Z! J* L* S8 f, }1 ]7 k( Q/ d- _    def LowWaveSelect(self, data):. w" b7 z3 G! A9 B. q! G4 x
        if not self.upLimit(self.inds[data][&#39;highestWave&#39;][0], self.inds[data][&#39;lowesstWave&#39;][0], self.p.LOW_WAVE_RATE):
7 q! ]6 l$ |- n' f' s            self.candidatesLowWave[data] = data.close[0]  #
  Z3 Y0 r7 S! r        #        self.log(&#34;添加lowest后选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))* k& S( ]$ H4 O7 J, X. R4 r
2 i) Z1 }! f5 i9 t. N9 j
        # 判断是否需要移除候选观察股池中的股票,在股票池超过stock_day个交易日" t- T; t! X. @7 o4 ]
        if data in self.candidatesLowWave:4 O9 h4 ~6 ^4 {3 m9 F
            if self.upLimit(self.inds[data][&#39;highestWave&#39;][0], self.inds[data][&#39;lowesstWave&#39;][0], self.p.LOW_WAVE_RATE):; ?* B4 U( ?* n: z% F
                self.candidatesLowWave.pop(data, None)
$ D6 ]) |  s5 J2 J
( @1 d" U. o8 J1 p: ?, o    #           self.log(&#34;移除Lowest后选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))
& x  J  u: C" L% m; L
) z- X2 |+ h0 T* J  t* ^4 O    def FisrtBanSelect(self, data, prev_close):4 j, M6 u7 i& t! x8 a3 K/ V1 Z
        if data in self.candidatesFirstBan:# h- X, b. j- H, b
            self.days_since_in_pool_firstBan[data] += 1' T& H1 x  n, T3 P. V

1 K( Z, l0 }5 M# E        current_date = self.data.datetime.date()
; T( M" m5 A3 f' v* V8 ]        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%
4 {+ f  o* A6 N! e! F# ^/ s9 o: C( ^            stock_name = data._name[2:]  # 获取股票名
$ {3 ]% b# @/ P* ]/ r            if stock_name not in self.candidatesFirstBan:
9 T  m, h1 K3 I" C4 j                self.candidatesFirstBan[data] = (current_date, prev_close)
! w& f2 ~' ^4 T                self.days_since_in_pool_firstBan[data] = 0
( Y5 _$ t, T( `6 B     #           self.log(&#34;添加首版选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))
& u% J4 w' P8 P( e# u! ~$ ?% @+ ^+ S4 s: Q, \, }& K+ u2 W4 |% y
        if data in self.candidatesFirstBan:
* s( \! i: j3 c/ Z. G" n; h: f6 z            if self.days_since_in_pool_firstBan[data] > 0:) _, B7 p& J- O: \, X. `
                self.candidatesFirstBan.pop(data, None)0 t4 J" N' c0 ?& p- l1 D( [
                self.days_since_in_pool_firstBan.pop(data, None). |3 n' [, f/ J
#               self.log(&#34;移除首版选股池&#34;, data._name, &#34;价格&#34;, data.close[0], data.datetime.date(0))' i: ^- D" `* V, l

- }) \2 A. E& G* E4 z) `+ Z! c: u7 a- k" T6 s% R' f
    def upLimit(self, currentPrice, lastClose, upLimit):
6 T! ~) y4 R1 [1 b/ Y4 W3 V        if currentPrice >= round(upLimit * lastClose + lastClose,2) :
7 o* P8 J/ P, C- C            return True+ c( t& P8 h& s4 t- v$ @' s! W
        else:
/ p/ w, o+ k' p# ^& j0 w            return False
! A/ w, g) {. Z' c* u1 G
2 j4 G8 i3 V' ?    def downLimit(self, currentPrice, lastClose, downLimit):) B3 A( @$ n. B3 b) v) ]) A
        if currentPrice - lastClose < downLimit * lastClose:% I/ I( H  \% q+ F! A
            return True
, d7 i; b! E1 R6 ?) K4 X        else:. ?4 k5 _9 @0 N  \- \8 h9 p
            return False
! h" Y) {( q' l6 Z- J1 X9 c
& O4 g; j6 `9 z    def next(self):. J! N1 m* e4 N* f. i
        for data in self.datas:
6 g" o( T! s9 F            if data not in self.first_day_traded:, X$ l2 U, o# Z( e. @5 p
                self.first_day_traded[data] = True
, d' E/ Q% D% Y                self.diff_start_dates_days[data] = 0
+ H5 d' q* Q  W* t7 J# l0 X6 ~                continue  # 跳过第一天交易判断
0 D6 k. X+ |/ d! s% T. s: B
$ I0 k0 ~' P, A            if data not in self.start_dates:9 i0 P  P9 J$ k, x7 }& v
                self.start_dates[data] = data.datetime.date(0)
" }$ e; x3 C6 N/ Q: Q                self.start_dates_flag[data] = False
1 G5 F6 X. D; P+ v, h3 j                continue
- p$ t- z! N- u5 `/ ~  o3 J            if (data.datetime.date(0) - self.start_dates[data]).days != 0 and  not self.start_dates_flag[data]:
: @+ h1 W* |0 F                self.start_dates_flag[data] = True
: g- G3 B& i, ^% e6 u# I                self.start_dates[data] = data.datetime.date(0). ?, X# K. P/ k5 C5 E
                continue
$ n6 m6 [# b' C9 h/ M# `) {, n2 k            # 当股票当前日大于起始日30天以上才进行计算,保证新股上市30天后才计算,2 Y4 E/ o% n0 S& j: m* x1 F' z
            if self.start_dates_flag[data] and (data.datetime.date(0) - self.start_dates[data]).days > 30 \- a7 N7 ^! H" d4 c6 N1 P; h
                    and (data.datetime.date(0) - self.start_dates[data]).days > self.diff_start_dates_days[data]:
8 @* }  ]; F! D8 H2 t* m                self.diff_start_dates_days[data] = (data.datetime.date(0) - self.start_dates[data]).days
7 }: Z0 r+ N, Z" l, k                #设置前一日价格
9 ~- K- g2 |" M) F( n$ k                dividend_flag = False
6 O1 V2 D4 l6 G: Z! n( Y2 K                prev_close = data.close[-1]
! v- z# V; J% n5 x/ b                if data.open[0] - prev_close < -0.11 * prev_close and data._name.startswith((&#39;sh60&#39;, &#39;sz00&#39;)) or \
5 A, }9 v; a/ F( R                        data.open[0] - prev_close < -0.21 * prev_close and data._name.startswith((&#39;sh68&#39;, &#39;sz30&#39;)):! V' h* z' X2 D& p+ {7 o
                    dividend_adjustment = prev_close / data.open[0]
$ l' r% i3 v: R: T, ?/ g9 @8 n                    prev_close /= dividend_adjustment/ R: |- H* e- @! f1 P" \2 S5 \
                    dividend_flag = True
. J; ^6 f: D! o7 M
3 _$ Q# ]; F, z" w+ w8 ?                self.FisrtBanSelect(data, prev_close)
. B- D" O2 N2 a0 b4 i4 D                self.LowWaveSelect(data)0 ~  B' b" d4 G  x( X
+ o! K  w; [9 i0 H
                # 判断是否需要卖出持仓股票
5 X1 O9 m8 Q4 f; B9 H6 ]* l                if data in self.positionsMy:, ^; a  [8 h3 \. K
                    self.days_since_in_host[data] += 1
9 P8 }* A% V2 }+ M1 L8 j                    #除权发生调整成本
9 u/ g* W' R" i! w                    if dividend_flag:: ~  s7 L7 j6 t2 Z! c
                        self.selfTrader.positions[data._name][&#39;price&#39;] /= dividend_adjustment
7 a6 O, K/ G, r  }# x# F& Y                        self.selfTrader.positions[data._name][&#39;quantity&#39;] *= dividend_adjustment/ Y, s/ ]9 l6 W8 w( s  A; n7 W/ D
                        self.log(&#34;除权发生&#34;, data._name, &#34;调整成本价&#34;, self.selfTrader.positions[data._name][&#39;price&#39;], data.datetime.date(0))
9 [4 G' G2 Z3 P. {) |. `
$ w- H( y, K  p$ V                    stock_name = data._name[2:]  # 获取股票名
% v- v) o- c5 D9 e                    stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)- K: b: y0 D) h) {# a: {% J0 i4 s
                    if os.path.isfile(stock_data_path):
; V7 |2 q7 P$ Q' Y* }                        df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;], index_col=&#39;date&#39;)) O) A* p0 W- D: G- I8 |5 H
                        next_date = data.datetime.date(0)
5 q; x- r$ k  j: R3 j# ^! i                        next_date_data = df[df.index.date == next_date]
  S: ~$ e9 ^( t$ M# K                        while next_date_data.empty:- m. N' z$ H' |6 Z% \& C9 R# G2 s+ e
                            next_date += datetime.timedelta(days=1); i) d9 V$ y: M: H
                            if next_date >= datetime.datetime.now().date():
6 s- x6 W* \! L/ ~                                self.log(&#34;异常:可能停牌&#34;, data._name, &#34;价格&#34;, &#34;&#34;,  data.datetime.date(0))
1 L" H8 Z+ R* S: h! r                                break3 H7 G( E' _9 z5 ]: S
                            next_date_data = df[df.index.date == next_date]2 Z: D5 F, t2 b! T0 {

! M0 J: C! Z  J7 V% V: v% I& u6 k                        highInterval = self.highIntervl(data, self.p.START_TIME, self.p.END_TIME)
1 O6 n; ]0 s/ x0 r                        close_price = (highInterval[0] - highInterval[1]) * 0.8 + highInterval[1]
7 S; [' i' l  c1 r# q: ^
/ j, C' p& ^# s1 y' K. b4 D                        if not self.upLimit(close_price, prev_close, self.p.UP_LIMIT):6 r+ L) d0 j% ~! j
                            sell_price = close_price* O- W6 i- Q" ^5 j
                            self.log(&#34;早盘卖&#34;, data._name, &#34;价格&#34;, sell_price, next_date.strftime(&#39;%Y-%m-%d&#39;))
$ c8 b9 p5 j* n& A3 h; }                            self.close(data=data, price=sell_price)9 K3 N8 d) i; P. U8 T
                            self.selfTrader.close(symbol=data._name, price=sell_price), m. g1 q. F, q* ]$ h; B: Q
                            self.positionsMy.pop(data, None)5 d- c8 E3 b9 b. L+ q) v+ u
                            # 必须要在卖出股票后移除candidates1股池,否则,卖出当天有可能还会进入买入逻辑,从而导致当天无法完成结算,而且反包规则卖出当天都不符合买入准则2 ^; ?+ x6 r/ Y
                            self.candidatesFirstBan.pop(data, None)$ w& I% e# q0 Z, E. W5 c
                        else:; }2 n7 Y$ Z: l/ G8 o7 E
                            self.log(&#34;涨停不卖&#34;, data._name, &#34;涨停价格&#34;, data.close[0], data.datetime.date(0))6 r# b6 G7 Z% o; m

- B: z1 ?/ V2 [6 \! Z/ A% v% F
8 N1 I# s, t5 w. r                # 判断是否在候选观察股池中,买入炸板股票根据规则
, y7 j6 I5 i% ~6 \) \3 q# j                if data in self.candidatesFirstBan and data in self.candidatesLowWave:3 o% D3 \/ U+ v" [
                    stock_name = data._name[2:]  # 获取股票名
2 C( a+ ^/ I; D4 u, |7 r; l3 A% {                    stock_data_path = os.path.join(self.p.Min_FOLDER, stock_name)
4 P6 Y2 W" F: C1 ]0 L0 V/ p+ v                    if os.path.isfile(stock_data_path):+ o5 n; s. e7 Y: v1 l1 B, B
                        df = pd.read_csv(stock_data_path, parse_dates=[&#39;date&#39;], index_col=&#39;date&#39;)8 H! o4 f! V; O4 ]
                        df = df[df.index.date == self.candidatesFirstBan[data][0]]  # 选择日期部分等于current_date的数据: G1 ~. R, I& M% b, D1 N
                        firstUpFlag = True( F1 E! s' E2 l9 k% ~( H
                        secondUpFlag = True
8 F$ @5 k7 w7 A/ U1 \6 d                        for index, row in df.iterrows():- z4 m/ ~6 f1 C& D/ C8 A
                            high = row[&#39;high&#39;]
8 C/ i: z( Q9 t7 H                            low = row[&#39;low&#39;]5 R% C9 `+ o9 Z4 C
                            datevalue = index.time()
) h/ o8 M4 c6 A: L
7 U7 @# R1 y& S9 i( Q7 A                            if self.upLimit(high, prev_close, self.p.UP_LIMIT) and firstUpFlag and secondUpFlag:
1 @) Q1 M& s7 l+ R: [' ?  [6 j                                # 首次涨停( c! ~4 Y" \! y& s. u
                                firstUpFlag = False7 j  J( I; b7 M0 Y
                                continue
1 @4 U) }0 j0 Z8 w$ Q5 x" a                            if self.upLimit(high, prev_close, self.p.UP_LIMIT) and not firstUpFlag and secondUpFlag and low < high:+ D# \% d& r5 c7 C% V
                                # 打开涨停版
8 m5 I$ D, l5 K: D                                secondUpFlag = False
0 x% U; R7 c; t$ ^, j                                continue
, ]  N, p' r) y4 ]2 v4 A                            if not firstUpFlag and self.upLimit(high, prev_close, self.p.UP_LIMIT) and low < high and not secondUpFlag :
' ]1 Q( w: F2 W                                # 再次准备涨停8 j1 O1 S  M" g$ Q. @+ l
                                cash = self.broker.getcash()" ]7 l+ N7 c  N/ [: Q+ M* R3 Y
                                if cash < 10000:
9 Z5 P$ u) b% a  y8 d6 t2 z" I                                    self.log(&#34;资金不足&#34;, data._name, &#34;持有股数量&#34;, len(self.positionsMy),
7 }0 w2 ^/ B) I( f                                             data.datetime.date(0))
* }* q6 w. `+ U8 c/ ~2 x( F1 A0 Y                                    self.count = self.count + 1
) Y1 V, w2 A1 Z! c8 v. S                                elif data not in self.positionsMy:
  |5 h% m* t: G8 V' A0 }+ s: O                                    buyPrice = round(prev_close * 1.1 ,2)
( N) q; q4 o, G. d                                    sizeOpen = 10000 / buyPrice' Z5 `: Y0 l3 i4 z0 ]5 L
                                    self.buy(data=data, size=sizeOpen)
) z5 z5 C. i$ E) `3 V6 `                                    self.positionsMy[data] = buyPrice6 O" |3 ^2 h8 M: D4 f2 ~) R9 u) {
                                    self.log(&#34;买入股票&#34;, data._name, &#34;买入价格&#34;, buyPrice, index.strftime(&#39;%Y-%m-%d %H:%M:%S&#39;))+ L3 v+ p7 I% m7 x9 B
                                    self.days_since_in_host[data] = 1
! J# l' U4 q. A8 H  n2 b                                    sizeClose = 10000 / buyPrice
. z5 p$ C: C% q: d& L7 J                                    self.selfTrader.buy(symbol=data._name, price=buyPrice, quantity=sizeClose)
& w: V" T! F" z, K8 H9 y1 i  H9 G% }                                break( D2 M7 ?) ]: O

" z  ], {4 l2 ?$ x- k) \
3 C$ z( X# M2 `" h7 S* r( _3 m$ Enow = datetime.datetime.now()* A& ?8 n8 I5 k# P- {4 Z7 L: L
one_day = datetime.timedelta(days=1)  {' O8 g6 a* |! ~# R7 o: u
next_day = now + one_day
/ ~$ L: D' C( h0 V/ ^! @class MyData(bt.feeds.GenericCSVData):
. L- @; O5 k! J+ r5 n    params = (
; i+ @. e6 s; A0 K7 V& \9 D. _% @        (&#39;dtformat&#39;, &#39;%Y-%m-%d&#39;),
1 g' v; u. I; m4 G8 w# P7 x        (&#39;datetime&#39;, 0),
3 ?. s' U  z6 a; X4 s        (&#39;open&#39;, 1),
( V0 o& G7 E* n, z3 E4 a2 {        (&#39;high&#39;, 2),
, P3 i7 ?% V: m& u8 o; }$ u        (&#39;low&#39;, 3),: a) {: _/ {  R* d* v7 ]7 J: Q+ C
        (&#39;close&#39;, 4),2 e" z9 A$ z4 N' P- _# w
        (&#39;volume&#39;, 6),
' `1 |- f: Y6 w0 ~4 ^        (&#39;openinterest&#39;, -1),
$ {& L3 a5 ^0 W0 a5 W) T/ M        (&#39;fromdate&#39;, datetime.datetime(2022, 12, 1)),
. S: s- w, r* @' W        (&#39;todate&#39;, next_day),0 U! C$ r& n. I+ K
    )
3 p. _& |' X. \4 N
* Z6 v( Z3 N) ]7 [6 mif __name__ == &#39;__main__&#39;:
- l; X+ p6 A4 \+ `& W) ]5 M    time_start = time.time()3 [  M% ?0 K6 ^0 q9 Y# b
    cerebro = bt.Cerebro(stdstats=False, cheat_on_open=False)
9 N8 P5 @/ q8 j4 k+ D4 N* b& }. _3 w$ B
    # 添加数据源( A) v' y* ]! y0 U9 p
    dataFolder = r&#39;/Users/Downloads/tdx/baostock&#39;# E0 e9 F, L3 @
    allStockList = r&#39;../in/all_stocks.csv&#39;- s8 q4 O4 r4 d* K) I& N9 x
    df_all = pd.read_csv(allStockList, header=0, na_values=&#39;NA&#39;)9 x9 D( `7 x/ m/ G) Y
+ y4 t: |% h1 M$ W+ m
    stock_lists = os.listdir(dataFolder)
7 L9 y, U3 C2 ~4 ]* ]! `/ r% O  ~+ i) I. ]
#    stock_lists = [&#39;sh600132.csv&#39;]( p& d+ y! D3 l7 K% d6 K
    for stock in stock_lists:
  \6 `1 f: F" X3 m8 D1 U- F( s        name = stock.replace(&#34;.csv&#34;, &#34;&#34;)
6 _: B0 A% R2 T" R        new_df = df_all[df_all[&#39;代码&#39;] == name]
: W, ]* G$ K+ Q- `% z+ n% Q. i9 c        if len(new_df) > 0:( ~6 H( q/ e8 i
            first_row = new_df.iloc[0]( p* y- e7 N+ \$ P' e8 E+ ^! f: v
            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;]\
4 w$ S$ ]) G; {: a' t                    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;] :
  p6 x: ?3 ]& g    #              and first_row[&#39;市盈率&#39;] > 0 and first_row[&#39;市盈率&#39;] < 100:
# _- N+ I- v3 N1 d9 C) R                filepath = os.path.join(dataFolder, f&#39;{stock}&#39;)
; |3 O; F3 s" S, ?1 h. G, W                size = os.path.getsize(filepath)
9 B1 G8 \  [/ V% O% n                if size > 10 * 200:
# k  t' c- Q% A6 F: P' b* R                    if stock.startswith((&#39;sh60&#39;, &#39;sz00&#39;)):
- i* @4 K2 }7 |# Y! E% V7 j                        df = pd.read_csv(filepath)
6 I) t  ?, x/ _1 \  a6 w( {                        df[&#39;date&#39;] = pd.to_datetime(df[&#39;date&#39;], format=&#39;%Y-%m-%d&#39;)) ~' {8 e% S# p& ~; S& O
                        last_row = df.tail(1)3 m) I! D$ W( ]! R
                        if last_row[&#39;date&#39;].values[0] > np.datetime64(&#39;2023-05-18&#39;):, K7 }2 b1 w& i9 }5 U3 q
                            cerebro.adddata(MyData(dataname=filepath),  name = stock)  G6 j2 t' {: _  c5 g
% o, |) a2 A+ R9 [
    # 添加交易记录
+ ]3 T3 Q5 s* ^! l    cerebro.broker.setcash(10000000.0)0 _. B& s* F5 I, Q  o
    cerebro.broker.setcommission(commission=0.0007)
3 Y# ]( q& s# w) s0 Y1 [! `3 X1 H9 g/ k- c0 j* T: p+ t
    cerebro.addstrategy(ZhaBanSelect)
/ w% g) U5 |" T9 |- b5 B" `! l6 c9 a0 i$ l: i  v$ A/ U7 _2 w
    cerebro.run()
0 @) ]$ B/ T3 t6 F    time_end = time.time()7 \' n3 V! \: L1 C2 }2 {

7 t- u% Y4 B3 f# z$ A3 i$ `9 \* C    print(&#39;程序所耗时间:&#39;, time_end - time_start)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

11

金钱

0

收听

0

听众
性别
保密

新手上路

金钱
11 元