歡迎來到 程式交易實戰 的第八堂課,還記得上堂課實作的均線加碼策略嗎?在策略精進的議題中,加減碼機制以及停損停利都扮演著相當重要的角色!前幾堂課舉了不少加碼策略的回測及實作範例,讓大家了解加碼機制的重要性,接著我們將進入停損停利的部分,介紹如何結合 LINE Notify 來接收停損停利通知,並搭配庫存進行實際演練。
讀完本篇文,您將學會...
- 結合 LINE Notify 接收停損停利通知
- 透過庫存個股進行停損停利實單演練
停損停利簡介
不知道大家在投資股票的過程中,是否也覺得賣股票比買股票還要困難很多?在主觀交易中,停損停利說起來相當容易,但真正執行時,容易因主觀判斷,而錯失賣出時機。因此,本篇文章將帶大家實作停損停利,透過程式交易來戰勝凹單的心魔!因考量到用戶可能會希望先以接收通知的方式確認策略可行性,因此本篇將介紹如何結合 LINE Notify 接收停損停利通知,並搭配庫存進行實單演練。
事前準備
在使用 LINE Notify 接收通知前,需先取得 LINE Notify 個人存取權杖 (Access Token),可以透過以下步驟:
- 登入 Line-Notify-Bot
- 到發 行存取權杖(開發人員用)點選發行權杖
- 填寫權杖名稱、選擇接收通知的聊天室,按下發行
- 點選複製,因 Token 不會再顯示,請記得留存該 Token
示意圖如下:
策略執行
準備好 LINE Token 後,我們廢話不多說,直接進入程式碼部分吧!
step1. 載入相關套件
# 載入交易 API 套件
from fugle_realtime import (HttpClient,WebSocketClient)
from configparser import ConfigParser
from fugle_trade.sdk import SDK
from fugle_trade.order import OrderObject
from fugle_trade.constant import (APCode, Trade, PriceFlag, BSFlag, Action)
# 載入相關套件
import time
import json
import math
import datetime
import requests
# 載入 line-notify 套件
import lineTool
step2. 設定參數
symbolId = '6285' # 可以輸入您庫存中的某檔個股
SP = 0.3 # 設定 30% 停利
SL = 0.2 # 設定 20% 停損
# 因考量讀者可能尚未開戶 or 目前無庫存可在這裡測試,可自行更改成本價
price_avg = 70
qty = 2000 # 2 張
step3. 程式碼實作
class SPSL_Strategy:
# 設定方便調整的參數有 股票代碼
def __init__(self, symbol_id, stopProfit_ratio, stopLoss_ratio):
# 初始化交易 API 並取得 sdk object
self.sdk = self._init_fugle_trade()
self.API_TOKEN = "INPUT_YOUR_API_TOKEN" # 輸入您的富果 API Token -> 取自 https://developer.fugle.tw/docs/key/
self.LINE_NOTIFY_TOKEN = "INPUT_YOUR_LINE_TOKEN" # 輸入您的 LINE Token -> 取自剛剛事前準備申請的 LINE Token
# 設定交易標的
self.symbol = symbol_id
# 設定停利比例
self.stopProfit_ratio = stopProfit_ratio
# 設定停損比例
self.stopLoss_ratio = stopLoss_ratio
# 最新價格
self.now_price = None
# 交易 API 設定檔的部分
def _init_fugle_trade(self):
# 讀取設定檔
config = ConfigParser()
config.read('./config.simulation.ini') # 使用模擬環境
# 登入
sdk = SDK(config)
sdk.login()
return sdk
# 賣出設定
def sell(self, qty, PriceFlag_type, APCode_type):
order = OrderObject(
buy_sell=Action.Sell,
price_flag=PriceFlag[PriceFlag_type],
price = '',
stock_no=self.symbol,
quantity=qty,
ap_code=APCode[APCode_type],
trade=Trade.Cash # 現股賣出 的交易類別
)
self.sdk.place_order(order)
# 平倉 -> 已考量整零股
def sell_total_shares(self, qty):
if qty >= 1000:
# 計算預計賣出張數 -> 小數點無條件捨去
vol = math.floor(qty/1000)
# 市價、整股賣
self.sell(vol, 'Market', 'Common')
# 若還有零股需再下零股單
# 賣出剩下的零股
odd_vol = qty - vol*1000
if odd_vol > 0:
# 零股無法用市價賣,因此使用限價-跌停價、盤中零股賣
self.sell(odd_vol, 'LimitDown', 'IntradayOdd')
# 庫存不足一張時
elif qty < 1000:
# 跌停價、盤中零股賣
self.sell(qty, 'LimitDown', 'IntradayOdd')
# 取得庫存 - 成交均價、成交股數
def get_inventoryInfo(self):
inventories_list = self.sdk.get_inventories()
# 取得成交均價
spec_symbol = list(filter(lambda x:x['stk_no']==self.symbol, inventories_list))
price_avg = float(spec_symbol[0]['price_avg'])
qty = int(spec_symbol[0]["cost_qty"])
return price_avg, qty
# 透過下面的 webSocket function 取得最新價格
def get_latest_price(self, message):
json_data = json.loads(message)
# 只使用整股最新價格
if json_data['data']['info']['type'] == "EQUITY":
# 更新目前價格
self.now_price = json_data['data']['quote']['trade']['price']
print(self.now_price)
def create_ws_quote(self):
ws_client = WebSocketClient(api_token=self.API_TOKEN)
ws = ws_client.intraday.quote(symbolId=self.symbol, on_message=self.get_latest_price)
ws.run_async()
time.sleep(1)
# 停損停利判斷
def run_strategy(self, price_avg, qty):
# 若已有庫存,可以拿掉以下註解取得庫存資訊
# price_avg, qty = self.get_inventoryInfo()
while datetime.time(9, 0, 0) < datetime.datetime.now.time() < datetime.time(13, 25, 0) and self.now_price is not None:
# 達到 停利 就進行平倉操作:
if self.now_price >= price_avg*(1+self.stopProfit_ratio):
# 執行平倉操作
self.sell_total_shares(qty)
# 預計回傳 line notify 通知的訊息
msg = '\n'+ f'達到停利條件:{self.stopProfit_ratio*100} %'+'\n'+'賣出' + str(self.symbol) +'\n'+ str(self.now_price) +'元' + '\n' + str(qty) + "股"
print(msg)
# 執行 line notify 通知
lineTool.lineNotify(self.LINE_NOTIFY_TOKEN, msg)
break
# 達到 停損條件 進行平倉操作
elif self.now_price <= price_avg*(1-self.stopLoss_ratio):
# 執行平倉操作
self.sell_total_shares(qty)
# 預計回傳 line notify 通知的訊息
msg = '\n'+ f'達到停損條件:-{self.stopLoss_ratio*100} %'+'\n'+'賣出' + str(self.symbol) +'\n'+ str(self.now_price) +'元' + '\n' + str(qty) + "股"
print(msg)
# 執行 line notify 通知
lineTool.lineNotify(self.LINE_NOTIFY_TOKEN, msg)
break
else:
msg = '\n'+ f'尚未達到停損停利條件!'+'\n'+'庫存成交均價:'+ str(price_avg) +' 元'+'\n'+'現價:' + str(self.now_price) +' 元' + '\n' +'股數:'+ str(qty) + " 股"
print(msg)
if __name__ == '__main__':
# 透過 step2. 設定的參數取得預計交易的標的、停利比例、停損比例
SPSL = SPSL_Strategy(symbolId, SP, SL)
SPSL.create_ws_quote()
SPSL.run_strategy(price_avg, qty)
若該股票達到您設定的停損停利條件,即可至您選擇的聊天室查看該訊息,示意圖如下:
結論
本篇文章帶大家了解如何實作基本的停損停利出場策略,並結合 LINE Notify 接收停損停利通知,搭配庫存個股進行停損停利的實作演練,希望結合個人化通知,幫助讀者漸進式地了解富果 API 的相關應用!另外,讀者也可以嘗試若再搭配分批出場該如何實作,快去試試吧!
下篇文章將會是停損停利的延伸應用,帶大家實作能夠因應市場變化而自動調整的進階版停損停利策略 - 移動停利停損,請大家敬請期待!