Created
June 16, 2024 08:39
-
-
Save lawrence910426/048767d3f13aee67f56db779fa905a25 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
import logging | |
import math | |
import os | |
import contextlib | |
import concurrent | |
import math | |
from decimal import * | |
from notify import Notify | |
import shioaji as sj | |
class Automata: | |
def __init__(self, sj_api, observe_id, trade_id): | |
self.sj_api = sj_api | |
self.observe_id = observe_id | |
self.trade_id = trade_id | |
self.observed_bulk_long, self.observed_bulk_short = False, False | |
self.order, self.entry_time = None, 0 | |
self.bid_price, self.ask_price = None, None | |
self.last_update = 0 | |
self.state = "INIT" | |
self.previous_state = self.state | |
self.market_orders, self.limit_down = 0, 0 | |
def loop(self): | |
if self.state == "INIT": | |
""" | |
The automata is initialized and everything is resetted. | |
""" | |
self.state = "WAIT_BULK_ORDER" | |
elif self.state == "WAIT_BULK_ORDER": | |
""" | |
Waiting for bulk order to arrive. | |
""" | |
if self.observed_bulk_long: | |
self.state = "PLACE_LONG_ORDER" | |
self.observed_bulk_long = False | |
if self.observed_bulk_short: | |
self.state = "PLACE_SHORT_ORDER" | |
self.observed_bulk_short = False | |
elif self.state == "PLACE_LONG_ORDER": | |
""" | |
Place a long order on trading instrument. | |
""" | |
if self.ask_price is not None: | |
self.order = self.place_order( | |
self.ask_price, 1, "BUY" | |
) | |
Notify.notify(f"Placed a buy order on {self.trade_id}") | |
self.entry_time = time.time() | |
self.state = "WAIT_DEAL" | |
elif self.state == "PLACE_SHORT_ORDER": | |
""" | |
Place a short order on trading instrument. | |
""" | |
if self.bid_price is not None: | |
self.order = self.place_order( | |
self.bid_price, 1, "SELL" | |
) | |
Notify.notify(f"Placed a sell order on {self.trade_id}") | |
self.entry_time = time.time() | |
self.state = "WAIT_DEAL" | |
elif self.state == "WAIT_DEAL": | |
""" | |
Wait until our order is dealt | |
""" | |
if self.order.status.status in [sj.constant.Status.Inactive]: | |
self.sj_api.update_status(trade=self.order) | |
if self.order.status.status in [sj.constant.Status.Filled]: | |
self.state = "BUILT" | |
if self.entry_time + 5 < time.time(): | |
self.state = "CANCEL_ORDER" | |
elif self.state == "CANCEL_ORDER": | |
""" | |
Cancel our placed order. | |
""" | |
self.sj_api.cancel_order(self.order) | |
self.sj_api.update_status(trade=self.order) | |
status = self.order.status.status | |
if status in [sj.constant.Status.Filled]: | |
self.state = "BUILT" | |
else: | |
self.state = "INIT" | |
elif self.state == "BUILT": | |
""" | |
Our order is filled. Show a notification. | |
""" | |
Notify.notify(f"Position is built on {self.trade_id}") | |
self.state = "IDLE" | |
elif self.state == "IDLE": | |
""" | |
Idle state. Nothing to do. | |
""" | |
pass | |
if self.previous_state != self.state: | |
logging.warning(f"State changed from {self.previous_state} to {self.state}") | |
self.previous_state = self.state | |
def place_order(self, price, size, side): | |
logging.info(f"Start placing order on {self.trade_id}") | |
order = self.sj_api.Order( | |
price=price, | |
quantity=size, | |
action=sj.constant.Action.Buy if side == "BUY" else sj.constant.Action.Sell, | |
price_type=sj.constant.StockPriceType.LMT, | |
order_type=sj.constant.OrderType.ROD, | |
order_lot=sj.constant.StockOrderLot.Common, | |
account=self.sj_api.stock_account, | |
daytrade_short=False if side == "BUY" else True | |
) | |
logging.warning(f"Placing order: {order}") | |
result = self.sj_api.place_order(self.sj_api.Contracts.Stocks[self.trade_id], order, timeout=0) | |
logging.warning(f"Place order result: {result}") | |
return result | |
def handle_quote_message(self, exchange, quote): | |
# Ignore the signal if simtrade | |
if quote["simtrade"] == 1: | |
return | |
timestamp = quote["datetime"].timestamp() * (10 ** 6) # In microseconds | |
if timestamp < self.last_update: | |
return | |
self.last_update = timestamp | |
if quote["code"] == self.trade_id: | |
self.bid_price = quote.bid_price[0] | |
self.ask_price = quote.ask_price[0] | |
if quote["code"] == self.observe_id: | |
if quote["volume"] >= 100: | |
if quote["tick_type"] == 1: | |
self.observed_bulk_long = True | |
if quote["tick_type"] == 2: | |
self.observed_bulk_short = True | |
# To use | |
# 1. automata = Automata(...) | |
# 2. while True: automata.loop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment