策略系列篇(一)—小市值轮动
前言
寫于2022年一月,臨近春節,祝大家新年快樂!寫這篇文章的時候,剛經歷了1.25號4000+只股票下跌,雖然入市不久,倉位也僅控制在3層,但是仍然缺乏安全感。于是提筆把入市半年的諸多策略以及成長記錄下來。
首先,投資就是一場戰爭。所有人入市的初心都是為了賺錢,但是市場是一個不可預知的混沌模型,小到幾百塊的資金,大到國家資本,無數個資金相互交織組成一個金融戰場。既然是戰場就會有廝殺,就會有勝負,有的人凱旋而歸,有的人戰死沙場。盡管金融市場不是真正的打仗,不會直接性的造成死亡,但是本質亦不遠矣。試想有的人辛辛苦苦積攢下來的積蓄,跑不過通貨膨脹,購買力甚至還在下降,只能維持溫飽而已;試想有的城市通過政策補貼吸引人才,完成轉型升級,然后實現飛躍發展;試想有的國家肆意的開放金融市場,經濟的飛速發展到盛極而衰。小到個體,大到城市,甚至是國家,投資皆是一場戰爭。
其次,投資需要好策略和好心態。兵者,國之大事,死生之地,存亡之道,不可不察也。戰爭,是國家的大事,它關系到軍民的生死,國家的存亡,不能不慎重考察研究。
投資需要好策略。舉個例子,就像是戰場時期,1000士兵對上10倍于己的平民肯定是屠殺。為什么呢?顯而易見,士兵之于平民,勝在訓練組織能力,進退有度,指揮統一,反之,平民雖然人數眾多,但是雜亂無序,只能四散而逃,之所以士兵能有較高水平的訓練組織能力,依賴于平時的針對訓練,我認為對士兵所做的訓練,在投資中類似于根據歷史數據來進行訓練學習,找出特征因子,最終形成一個好的交易策略,應用于市場中。
投資需要好心態。舉個例子,剛訓練好的新兵第一次上戰場的傷亡概率最高,而老兵損傷往往要小得多。顯而易見,不能適應戰場的新兵都已經陣亡了,活下來的老兵或多或少都有領悟。盡管新兵老兵在平時訓練時是一樣的,但是戰場上為什么會有如此巨大的差別呢?我認為取決于是否有好的心態,市場瞬息萬變,沒有人能肯定未來會發生什么,每逢大事當有靜氣。在做足準備的情況下,需要堅定不移的相信自己的判斷,同時要具有底線思維。
最后,好策略和好心態是相輔相成的。如果沒有好的交易策略,長期無邏輯的交易,是不可能有好心態的;如果有好的交易策略,沒有良好的心態,也難以執行。
好的策略需要自身不斷地迭代更新,需要不斷的學習知識,提升認知;而好的心態需要市場不斷地錘煉打磨,需要進行體會總結。
一個完整的交易策略一般包括如下幾點構成:
????????1.買什么? 選股
????????2.買賣多少?倉位控制
????????3.何時買賣?入場時機
????????4.止損條件。何時退出虧損的倉位
????????5.止贏條件。何時退出盈利的倉位
廢話完畢,正文開始!
---------------分割線---------------
1. 原理說明
小市值效應,本質上說就是市值小的股票,在將來上漲的概率越大。在全球各大市場上都有驗證,在A股市場也存在此效應。
至于為什么有小市值效應?筆者推測有如下3點:
1.可能是投資者普遍不愿意持有小公司股票,使得這些小公司價格普遍偏低,甚至低于成本價,因此會有較高的預期收益率;
2.A股市場存在上市公司的殼價值,可能被溢價收購,導致小市值公司股票大幅度增值;
3.小市值公司成長空間更大,方便炒作;
2. 策略邏輯
第一步:確定調倉頻率,每天更新調倉
第二步:確定股票池股票數量,這里假設有5支
第三步:調倉日當天獲取前一天的歷史數據,并按照市值由小到大排序
第四步:買入前5支股票
回測期:2011-12-30?到 2021-12-31
股票池:中小板股票
回測初始資金:10萬
3. 回測結果和穩健性分析
回測時間為近10年,收益高達442倍,最大回測僅32%。
設定初始資金10萬,手續費率為0.03%,滑點比率為0.1%。回測結果如下圖所示:
筆者自身券商的費率是萬1的,開戶早還能免5,萬3的手續費長期來看其實吃了不少收益。
4. 策略代碼
# 導入函數庫 from jqdata import *# 初始化函數,設定基準等等 def initialize(context):# 設定滬深300作為基準set_benchmark('000300.XSHG')# 開啟動態復權模式(真實價格)set_option('use_real_price', True)#防止未來函數set_option("avoid_future_data", True)#滑點設置為0.001set_slippage(FixedSlippage(0.001))# 輸出內容到日志 log.info()log.info('初始函數開始運行且全局只運行一次')# 過濾掉order系列API產生的比error級別低的loglog.set_level('order', 'error')# 股票池g.security_universe_index = "399101.XSHE" # 中小板g.buy_stock_count = 5### 股票相關設定 #### 股票類每筆交易時的手續費是:買入時傭金萬分之三,賣出時傭金萬分之三加千分之一印花稅, 每筆交易傭金最低扣5塊錢set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5),type='stock')## 運行函數(reference_security為運行時間的參考標的;傳入的標的只做種類區分,因此傳入'000300.XSHG'或'510300.XSHG'是一樣的)# 定時運行run_daily(my_trade, time='9:30', reference_security='000300.XSHG')# 收盤后運行run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')## 開盤時運行函數 def my_trade(context):# 選取中小板中市值最小的若干只check_out_lists = get_index_stocks(g.security_universe_index)q = query(valuation.code).filter(valuation.code.in_(check_out_lists)).order_by(valuation.circulating_market_cap.asc()).limit(g.buy_stock_count * 3)check_out_lists = list(get_fundamentals(q).code)# 過濾: 三停(停牌、漲停、跌停)及st,*st,退市check_out_lists = filter_st_stock(check_out_lists)check_out_lists = filter_limitup_stock(context, check_out_lists)check_out_lists = filter_limitdown_stock(context, check_out_lists)check_out_lists = filter_paused_stock(check_out_lists)# 取需要的只數check_out_lists = check_out_lists[:g.buy_stock_count]# 買賣adjust_position(context, check_out_lists)## 收盤后運行函數 def after_market_close(context):log.info(str('函數運行時間(after_market_close):' + str(context.current_dt.time())))# 得到當天所有成交記錄trades = get_trades()for _trade in trades.values():log.info('成交記錄:' + str(_trade))log.info('一天結束')log.info('##############################################################')# 自定義下單 # 根據Joinquant文檔,當前報單函數都是阻塞執行,報單函數(如order_target_value)返回即表示報單完成 # 報單成功返回報單(不代表一定會成交),否則返回None def order_target_value_(security, value):if value == 0:log.debug("Selling out %s" % (security))else:log.debug("Order %s to value %f" % (security, value))# 如果股票停牌,創建報單會失敗,order_target_value 返回None# 如果股票漲跌停,創建報單會成功,order_target_value 返回Order,但是報單會取消# 部成部撤的報單,聚寬狀態是已撤,此時成交量>0,可通過成交量判斷是否有成交return order_target_value(security, value)# 開倉,買入指定價值的證券 # 報單成功并成交(包括全部成交或部分成交,此時成交量大于0),返回True # 報單失敗或者報單成功但被取消(此時成交量等于0),返回False def open_position(security, value):order = order_target_value_(security, value)if order != None and order.filled > 0:return Truereturn False# 平倉,賣出指定持倉 # 平倉成功并全部成交,返回True # 報單失敗或者報單成功但被取消(此時成交量等于0),或者報單非全部成交,返回False def close_position(position):security = position.securityorder = order_target_value_(security, 0) # 可能會因停牌失敗if order != None:if order.status == OrderStatus.held and order.filled == order.amount:return Truereturn False# 交易 def adjust_position(context, buy_stocks):for stock in context.portfolio.positions:if stock not in buy_stocks:log.info("stock [%s] in position is not buyable" % (stock))position = context.portfolio.positions[stock]close_position(position)else:log.info("stock [%s] is already in position" % (stock))# 根據股票數量分倉# 此處只根據可用金額平均分配購買,不能保證每個倉位平均分配position_count = len(context.portfolio.positions)if g.buy_stock_count > position_count:value = context.portfolio.cash / (g.buy_stock_count - position_count)for stock in buy_stocks:if context.portfolio.positions[stock].total_amount == 0:if open_position(stock, value):if len(context.portfolio.positions) == g.buy_stock_count:break# 過濾停牌股票 def filter_paused_stock(stock_list):current_data = get_current_data()return [stock for stock in stock_list if not current_data[stock].paused]# 過濾ST及其他具有退市標簽的股票 def filter_st_stock(stock_list):current_data = get_current_data()return [stock for stock in stock_listif not current_data[stock].is_stand 'ST' not in current_data[stock].nameand '*' not in current_data[stock].nameand '退' not in current_data[stock].name]# 過濾漲停的股票 def filter_limitup_stock(context, stock_list):last_prices = history(1, unit='1m', field='close', security_list=stock_list)current_data = get_current_data()# 已存在于持倉的股票即使漲停也不過濾,避免此股票再次可買,但因被過濾而導致選擇別的股票return [stock for stock in stock_list if stock in context.portfolio.positions.keys()or last_prices[stock][-1] < current_data[stock].high_limit]# return [stock for stock in stock_list if stock in context.portfolio.positions.keys() # or last_prices[stock][-1] < current_data[stock].high_limit * 0.995]# 過濾跌停的股票 def filter_limitdown_stock(context, stock_list):last_prices = history(1, unit='1m', field='close', security_list=stock_list)current_data = get_current_data()return [stock for stock in stock_list if stock in context.portfolio.positions.keys()or last_prices[stock][-1] > current_data[stock].low_limit]5. 策略思考以及改進
策略優勢:收益高,持續有效;
策略不足:高波動性,收益曲線不平滑;
眾所周知,高波動極其考驗投資者的執行力,往往大家都會高估自己的承受能力。
6. 后續其他
策略源碼如上,喜歡的朋友可以自己運行一下程序。
后續有時間我也會陸續更新其他策略,持續學習,持續分享。
注:此策略只用于學習、交流、演示,不構成任何投資建議。
作者公眾號:小z定投錄,可以關注一下,后面將不定期出新策略。
?
總結
以上是生活随笔為你收集整理的策略系列篇(一)—小市值轮动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 举个栗子!Tableau技巧(38):快
- 下一篇: 田静语法总结