@@ -26,9 +26,9 @@ def get_state_doc_ref(*, collection="strategy", document="MULTI_ASSET_STATE"):
2626 return get_firestore_client ().collection (collection ).document (document )
2727
2828
29- def load_trade_state (* , normalize_fn , default_state_factory , normalize = True , collection = "strategy" , document = "MULTI_ASSET_STATE" ):
29+ def load_trade_state (* , normalize_fn , default_state_factory , normalize = True , collection = "strategy" , document = "MULTI_ASSET_STATE" , store = None ):
3030 try :
31- payload = _get_document_store ().get (collection = collection , document_id = document )
31+ payload = ( store if store is not None else _get_document_store () ).get (collection = collection , document_id = document )
3232 if payload is not None :
3333 return normalize_fn (payload ) if normalize else payload
3434 return default_state_factory () if normalize else {}
@@ -37,16 +37,60 @@ def load_trade_state(*, normalize_fn, default_state_factory, normalize=True, col
3737 return None
3838
3939
40- def save_trade_state (data , * , normalize_fn , collection = "strategy" , document = "MULTI_ASSET_STATE" ):
40+ def save_trade_state (data , * , normalize_fn , collection = "strategy" , document = "MULTI_ASSET_STATE" , store = None ):
4141 try :
4242 persisted_state = normalize_fn (data )
43- _get_document_store ().set (collection = collection , document_id = document , data = persisted_state )
43+ ( store if store is not None else _get_document_store () ).set (collection = collection , document_id = document , data = persisted_state )
4444 return True
4545 except Exception :
4646 print (t ("firestore_write_failed" , error = "state_persistence_failed" ))
4747 return False
4848
4949
50+ def bind_trade_state_access (* , normalize_fn , default_state_factory ,
51+ collection = "strategy" , document = "MULTI_ASSET_STATE" ):
52+ """Bind this runtime's state and persistent owner to the same Firestore backend."""
53+ store = _get_document_store ()
54+
55+ def load (normalize = True ):
56+ return load_trade_state (normalize_fn = normalize_fn , default_state_factory = default_state_factory ,
57+ normalize = normalize , collection = collection , document = document , store = store )
58+
59+ def save (data ):
60+ return save_trade_state (data , normalize_fn = normalize_fn , collection = collection , document = document , store = store )
61+
62+ def owner_document ():
63+ return store .client .collection (collection ).document (document + "__owner" )
64+
65+ def claim (owner_id ):
66+ from google .api_core .exceptions import AlreadyExists
67+ if not isinstance (owner_id , str ) or not owner_id .strip ():
68+ raise ValueError ("state_owner_required" )
69+ try :
70+ owner_document ().create ({"owner_id" : owner_id }, retry = None )
71+ except AlreadyExists :
72+ return False
73+ return True
74+
75+ def release (owner_id ):
76+ from google .cloud import firestore
77+ if not isinstance (owner_id , str ) or not owner_id .strip ():
78+ raise ValueError ("state_owner_required" )
79+ ref = owner_document ()
80+
81+ @firestore .transactional
82+ def delete_owned (transaction ):
83+ snapshot = ref .get (transaction = transaction , retry = None )
84+ if not snapshot .exists or snapshot .to_dict ().get ("owner_id" ) != owner_id :
85+ return False
86+ transaction .delete (ref )
87+ return True
88+
89+ return delete_owned (store .client .transaction (max_attempts = 1 ))
90+
91+ return load , save , claim , release
92+
93+
5094def send_tg_msg (token , chat_id , text ):
5195 message = build_telegram_message (text )
5296 receipt = {
0 commit comments